[vm/ffi] Check `Array<T extends Struct>` bounds on `[]`

TEST=tests/ffi/inline_array_test.dart

Bug: https://github.com/dart-lang/sdk/issues/54379
Change-Id: I2799056ce48fa8cfa590512b241fe72f61a07dbf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/345060
Reviewed-by: Tess Strickland <sstrickl@google.com>
diff --git a/pkg/vm/lib/transformations/ffi/use_sites.dart b/pkg/vm/lib/transformations/ffi/use_sites.dart
index 37348b7..85b13b6 100644
--- a/pkg/vm/lib/transformations/ffi/use_sites.dart
+++ b/pkg/vm/lib/transformations/ffi/use_sites.dart
@@ -938,14 +938,43 @@
     final constructor = clazz.constructors
         .firstWhere((c) => c.name == Name("#fromTypedDataBase"));
 
-    final typedDataBasePrime = typedDataBaseOffset(
-        getArrayTypedDataBaseField(NullCheck(node.arguments.positional[0])),
-        multiply(node.arguments.positional[1], inlineSizeOf(dartType)!),
-        inlineSizeOf(dartType)!,
-        dartType,
-        node.fileOffset);
+    final arrayVar = VariableDeclaration("#array",
+        initializer: NullCheck(node.arguments.positional[0]),
+        type: InterfaceType(arrayClass, Nullability.nonNullable),
+        isSynthesized: true)
+      ..fileOffset = node.fileOffset;
+    final indexVar = VariableDeclaration("#index",
+        initializer: NullCheck(node.arguments.positional[1]),
+        type: coreTypes.intNonNullableRawType,
+        isSynthesized: true)
+      ..fileOffset = node.fileOffset;
 
-    return ConstructorInvocation(constructor, Arguments([typedDataBasePrime]));
+    return BlockExpression(
+      Block([
+        arrayVar,
+        indexVar,
+        ExpressionStatement(InstanceInvocation(
+          InstanceAccessKind.Instance,
+          VariableGet(arrayVar),
+          arrayCheckIndex.name,
+          Arguments([VariableGet(indexVar)]),
+          interfaceTarget: arrayCheckIndex,
+          functionType: arrayCheckIndex.getterType as FunctionType,
+        )),
+      ]),
+      ConstructorInvocation(
+        constructor,
+        Arguments([
+          typedDataBaseOffset(
+            getArrayTypedDataBaseField(VariableGet(arrayVar)),
+            multiply(VariableGet(indexVar), inlineSizeOf(dartType)!),
+            inlineSizeOf(dartType)!,
+            dartType,
+            node.fileOffset,
+          )
+        ]),
+      ),
+    );
   }
 
   /// Generates an expression that returns a new `Array<dartType>`.
@@ -1031,12 +1060,13 @@
       arrayVar,
       indexVar,
       ExpressionStatement(InstanceInvocation(
-          InstanceAccessKind.Instance,
-          VariableGet(arrayVar),
-          arrayCheckIndex.name,
-          Arguments([VariableGet(indexVar)]),
-          interfaceTarget: arrayCheckIndex,
-          functionType: arrayCheckIndex.getterType as FunctionType)),
+        InstanceAccessKind.Instance,
+        VariableGet(arrayVar),
+        arrayCheckIndex.name,
+        Arguments([VariableGet(indexVar)]),
+        interfaceTarget: arrayCheckIndex,
+        functionType: arrayCheckIndex.getterType as FunctionType,
+      )),
       singleElementSizeVar,
       elementSizeVar,
       offsetVar
diff --git a/tests/ffi/inline_array_test.dart b/tests/ffi/inline_array_test.dart
index fb92072..538b35f 100644
--- a/tests/ffi/inline_array_test.dart
+++ b/tests/ffi/inline_array_test.dart
@@ -19,6 +19,8 @@
   testToString();
   testRange();
   testRangeArrayOfPointer();
+  testRangeArrayOfArray();
+  testRangeArrayOfStruct();
 }
 
 void testSizeOf() {
@@ -109,3 +111,30 @@
   @Array(8)
   external Array<Pointer<Uint8>> a0;
 }
+
+void testRangeArrayOfArray() {
+  final pointer = calloc<StructWithArrayArray>();
+  final struct = pointer.ref;
+  final array = struct.a0;
+  array[0] = array[1];
+  Expect.throws(() => array[-1]);
+  Expect.throws(() => array[-1] = array[1]);
+  Expect.throws(() => array[2]);
+  Expect.throws(() => array[2] = array[1]);
+  calloc.free(pointer);
+}
+
+final class StructWithArrayArray extends Struct {
+  @Array(2, 2)
+  external Array<Array<Uint8>> a0;
+}
+
+void testRangeArrayOfStruct() {
+  final pointer = calloc<Struct4BytesInlineArrayMultiDimensionalInt>();
+  final struct = pointer.ref;
+  final array = struct.a0[0];
+  print(array[0]);
+  Expect.throws(() => array[-1]);
+  Expect.throws(() => array[2]);
+  calloc.free(pointer);
+}