[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);
+}