Extend test 712-varhandle-invocations to check negative array indices.

Check that varhandle intrisics do not crash and delegate to slow path
when array index is negative. The runtime should throw an out-of-bounds
exception, which is caught and ignored by the test. Not throwing an
exception fails the test as well.

Bug: 71781600
Test: art/test.py -r -t 712-varhandle-invocations --host
Test: art/test.py -r -t 712-varhandle-invocations --target
Test: Manually change/remove the generated instructions that check for
  negative array indices on arm64/x86_64 and ensure that the above test
  crash in the generated code, as expected.
Change-Id: Id2c31b639ab63fd1f3fb7d521f9e6a3676535fe5
diff --git a/test/712-varhandle-invocations/util-src/generate_java.py b/test/712-varhandle-invocations/util-src/generate_java.py
index 5d4bced..763fb20 100644
--- a/test/712-varhandle-invocations/util-src/generate_java.py
+++ b/test/712-varhandle-invocations/util-src/generate_java.py
@@ -576,6 +576,12 @@
     dictionary['lookup'] = var_handle_kind.get_lookup(dictionary)
     dictionary['field_declarations'] = ";\n".join(var_handle_kind.get_field_declarations(dictionary))
     dictionary['read_value'] = var_handle_kind.get_value(dictionary)
+
+    # For indexable types we need to check out-of-bounds access at negative index.
+    # We always generate the check, but comment it out for non-indexable types.
+    dictionary['coordinates_negative_index'] = coordinates.replace('index', '-16')
+    dictionary['indexable_only'] = "//" if not re.search('Array|ByteBuffer', var_handle_kind.name) else ""
+
     return dictionary
 
 def emit_accessor_test(var_handle_kind, accessor, var_type, output_path):
@@ -587,11 +593,21 @@
     if accessor.access_mode_form == AccessModeForm.GET:
         test_template = Template("""
         ${var_type} value = (${var_type}) vh.${accessor_method}(${coordinates});
-        assertEquals(${initial_value}, value);""")
+        assertEquals(${initial_value}, value);
+        // Check for out of bounds access (for indexable types only).
+        ${indexable_only} try {
+        ${indexable_only}   value = (${var_type}) vh.${accessor_method}(${coordinates_negative_index});
+        ${indexable_only}   failUnreachable();
+        ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
     elif accessor.access_mode_form == AccessModeForm.SET:
         test_template = Template("""
         vh.${accessor_method}(${coordinates}${updated_value});
-        assertEquals(${updated_value}, ${read_value});""")
+        assertEquals(${updated_value}, ${read_value});
+        // Check for out of bounds access (for indexable types only).
+        ${indexable_only} try {
+        ${indexable_only}   vh.${accessor_method}(${coordinates_negative_index}${updated_value});
+        ${indexable_only}   failUnreachable();
+        ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
     elif accessor.access_mode_form == AccessModeForm.STRONG_COMPARE_AND_SET:
         test_template = Template("""
         assertEquals(${initial_value}, ${read_value});
@@ -602,7 +618,12 @@
         // Test an update that should fail.
         applied = (boolean) vh.${accessor_method}(${coordinates}${initial_value}, ${initial_value});
         assertFalse(applied);
-        assertEquals(${updated_value}, ${read_value});""")
+        assertEquals(${updated_value}, ${read_value});
+        // Check for out of bounds access (for indexable types only).
+        ${indexable_only} try {
+        ${indexable_only}   applied = (boolean) vh.${accessor_method}(${coordinates_negative_index}${updated_value}, ${updated_value});
+        ${indexable_only}   failUnreachable();
+        ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
     elif accessor.access_mode_form == AccessModeForm.WEAK_COMPARE_AND_SET:
         test_template = Template("""
         assertEquals(${initial_value}, ${read_value});
@@ -617,7 +638,12 @@
         // Test an update that should fail.
         applied = (boolean) vh.${accessor_method}(${coordinates}${initial_value}, ${initial_value});
         assertFalse(applied);
-        assertEquals(${updated_value}, ${read_value});""")
+        assertEquals(${updated_value}, ${read_value});
+        // Check for out of bounds access (for indexable types only).
+        ${indexable_only} try {
+        ${indexable_only}   applied = (boolean) vh.${accessor_method}(${coordinates_negative_index}${updated_value}, ${updated_value});
+        ${indexable_only}   failUnreachable();
+        ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
     elif accessor.access_mode_form == AccessModeForm.COMPARE_AND_EXCHANGE:
         test_template = Template("""
         // This update should succeed.
@@ -627,23 +653,43 @@
         // This update should fail.
         witness_value = (${var_type}) vh.${accessor_method}(${coordinates}${initial_value}, ${initial_value});
         assertEquals(${updated_value}, witness_value);
-        assertEquals(${updated_value}, ${read_value});""")
+        assertEquals(${updated_value}, ${read_value});
+        // Check for out of bounds access (for indexable types only).
+        ${indexable_only} try {
+        ${indexable_only}   witness_value = (${var_type}) vh.${accessor_method}(${coordinates_negative_index}${updated_value}, ${updated_value});
+        ${indexable_only}   failUnreachable();
+        ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
     elif accessor.access_mode_form == AccessModeForm.GET_AND_SET:
         test_template = Template("""
         ${var_type} old_value = (${var_type}) vh.${accessor_method}(${coordinates}${updated_value});
         assertEquals(${initial_value}, old_value);
-        assertEquals(${updated_value}, ${read_value});""")
+        assertEquals(${updated_value}, ${read_value});
+        // Check for out of bounds access (for indexable types only).
+        ${indexable_only} try {
+        ${indexable_only}   old_value = (${var_type}) vh.${accessor_method}(${coordinates_negative_index}${updated_value});
+        ${indexable_only}   failUnreachable();
+        ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
     elif accessor.access_mode_form == AccessModeForm.GET_AND_UPDATE_BITWISE:
         if var_type.supports_bitwise == True:
             expansions['binop'] = accessor.get_java_bitwise_operator()
             test_template = Template("""
             ${var_type} old_value = (${var_type}) vh.${accessor_method}(${coordinates}${updated_value});
             assertEquals(${initial_value}, old_value);
-            assertEquals(${initial_value} ${binop} ${updated_value}, ${read_value});""")
+            assertEquals(${initial_value} ${binop} ${updated_value}, ${read_value});
+            // Check for out of bounds access (for indexable types only).
+            ${indexable_only} try {
+            ${indexable_only}   old_value = (${var_type}) vh.${accessor_method}(${coordinates_negative_index}${updated_value});
+            ${indexable_only}   failUnreachable();
+            ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
         else:
             test_template = Template("""
             vh.${accessor_method}(${coordinates}${initial_value}, ${updated_value});
-            failUnreachable();""")
+            failUnreachable();
+            // Check for out of bounds access (for indexable types only).
+            ${indexable_only} try {
+            ${indexable_only}   vh.${accessor_method}(${coordinates_negative_index}${updated_value}, ${updated_value});
+            ${indexable_only}   failUnreachable();
+            ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
     elif accessor.access_mode_form == AccessModeForm.GET_AND_UPDATE_NUMERIC:
         if var_type.supports_numeric == True:
             expansions['binop'] = accessor.get_java_numeric_operator()
@@ -651,11 +697,21 @@
             ${var_type} old_value = (${var_type}) vh.${accessor_method}(${coordinates}${updated_value});
             assertEquals(${initial_value}, old_value);
             ${var_type} expected_value = (${var_type}) (${initial_value} ${binop} ${updated_value});
-            assertEquals(expected_value, ${read_value});""")
+            assertEquals(expected_value, ${read_value});
+            // Check for out of bounds access (for indexable types only).
+            ${indexable_only} try {
+            ${indexable_only}   old_value = (${var_type}) vh.${accessor_method}(${coordinates_negative_index}${updated_value});
+            ${indexable_only}   failUnreachable();
+            ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
         else:
             test_template = Template("""
             vh.${accessor_method}(${coordinates}${initial_value}, ${updated_value});
-            failUnreachable();""")
+            failUnreachable();
+            // Check for out of bounds access (for indexable types only).
+            ${indexable_only} try {
+            ${indexable_only}   vh.${accessor_method}(${coordinates_negative_index}${updated_value}, ${updated_value});
+            ${indexable_only}   failUnreachable();
+            ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
     else:
         raise ValueError(accessor.access_mode_form)