[vm/ffi] Make elementAt an extension method

The CFE rewrite for structs, unions, and abi-specific integers is now
done on the extension methods.
The other native types are implemented in the extension methods
directly.

TEST=test/ffi

Closes: https://github.com/dart-lang/sdk/issues/50714
Change-Id: I578325733e3cd66200e80949d47ff12a13115b99
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/276102
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
This commit is contained in:
Daco Harkes 2022-12-16 16:33:12 +00:00 committed by Commit Queue
parent 3d3fdc7b46
commit 2435f0b398
6 changed files with 162 additions and 122 deletions

View file

@ -113,7 +113,7 @@ const Map<NativeType, int> nativeTypeSizes = <NativeType, int>{
NativeType.kBool: 1,
};
/// Load, store, and elementAt are rewired to their static type for these types.
/// Load and store are rewired to their static type for these types.
const List<NativeType> optimizedTypes = [
NativeType.kBool,
NativeType.kInt8,
@ -201,16 +201,19 @@ class FfiTransformer extends Transformer {
final Procedure allocatorAllocateMethod;
final Procedure castMethod;
final Procedure offsetByMethod;
final Procedure elementAtMethod;
final Procedure addressGetter;
final Procedure structPointerGetRef;
final Procedure structPointerSetRef;
final Procedure structPointerGetElemAt;
final Procedure structPointerSetElemAt;
final Procedure structPointerElementAt;
final Procedure structPointerElementAtTearoff;
final Procedure unionPointerGetRef;
final Procedure unionPointerSetRef;
final Procedure unionPointerGetElemAt;
final Procedure unionPointerSetElemAt;
final Procedure unionPointerElementAt;
final Procedure unionPointerElementAtTearoff;
final Procedure structArrayElemAt;
final Procedure unionArrayElemAt;
final Procedure arrayArrayElemAt;
@ -219,6 +222,8 @@ class FfiTransformer extends Transformer {
final Procedure abiSpecificIntegerPointerSetValue;
final Procedure abiSpecificIntegerPointerElemAt;
final Procedure abiSpecificIntegerPointerSetElemAt;
final Procedure abiSpecificIntegerPointerElementAt;
final Procedure abiSpecificIntegerPointerElementAtTearoff;
final Procedure abiSpecificIntegerArrayElemAt;
final Procedure abiSpecificIntegerArraySetElemAt;
final Procedure asFunctionMethod;
@ -246,7 +251,6 @@ class FfiTransformer extends Transformer {
final Map<NativeType, Procedure> loadUnalignedMethods;
final Map<NativeType, Procedure> storeMethods;
final Map<NativeType, Procedure> storeUnalignedMethods;
final Map<NativeType, Procedure> elementAtMethods;
final Procedure loadAbiSpecificIntMethod;
final Procedure loadAbiSpecificIntAtIndexMethod;
final Procedure storeAbiSpecificIntMethod;
@ -363,8 +367,6 @@ class FfiTransformer extends Transformer {
index.getProcedure('dart:ffi', 'Allocator', 'allocate'),
castMethod = index.getProcedure('dart:ffi', 'Pointer', 'cast'),
offsetByMethod = index.getProcedure('dart:ffi', 'Pointer', '_offsetBy'),
elementAtMethod =
index.getProcedure('dart:ffi', 'Pointer', 'elementAt'),
addressGetter =
index.getProcedure('dart:ffi', 'Pointer', 'get:address'),
compoundTypedDataBaseField =
@ -397,6 +399,10 @@ class FfiTransformer extends Transformer {
index.getProcedure('dart:ffi', 'StructPointer', '[]'),
structPointerSetElemAt =
index.getProcedure('dart:ffi', 'StructPointer', '[]='),
structPointerElementAt =
index.getProcedure('dart:ffi', 'StructPointer', 'elementAt'),
structPointerElementAtTearoff = index.getProcedure('dart:ffi',
'StructPointer', LibraryIndex.tearoffPrefix + 'elementAt'),
unionPointerGetRef =
index.getProcedure('dart:ffi', 'UnionPointer', 'get:ref'),
unionPointerSetRef =
@ -405,6 +411,10 @@ class FfiTransformer extends Transformer {
index.getProcedure('dart:ffi', 'UnionPointer', '[]'),
unionPointerSetElemAt =
index.getProcedure('dart:ffi', 'UnionPointer', '[]='),
unionPointerElementAt =
index.getProcedure('dart:ffi', 'UnionPointer', 'elementAt'),
unionPointerElementAtTearoff = index.getProcedure('dart:ffi',
'UnionPointer', LibraryIndex.tearoffPrefix + 'elementAt'),
structArrayElemAt = index.getProcedure('dart:ffi', 'StructArray', '[]'),
unionArrayElemAt = index.getProcedure('dart:ffi', 'UnionArray', '[]'),
arrayArrayElemAt = index.getProcedure('dart:ffi', 'ArrayArray', '[]'),
@ -418,6 +428,12 @@ class FfiTransformer extends Transformer {
index.getProcedure('dart:ffi', 'AbiSpecificIntegerPointer', '[]'),
abiSpecificIntegerPointerSetElemAt =
index.getProcedure('dart:ffi', 'AbiSpecificIntegerPointer', '[]='),
abiSpecificIntegerPointerElementAt = index.getProcedure(
'dart:ffi', 'AbiSpecificIntegerPointer', 'elementAt'),
abiSpecificIntegerPointerElementAtTearoff = index.getProcedure(
'dart:ffi',
'AbiSpecificIntegerPointer',
LibraryIndex.tearoffPrefix + 'elementAt'),
abiSpecificIntegerArrayElemAt =
index.getProcedure('dart:ffi', 'AbiSpecificIntegerArray', '[]'),
abiSpecificIntegerArraySetElemAt =
@ -462,10 +478,6 @@ class FfiTransformer extends Transformer {
return index.getTopLevelProcedure(
'dart:ffi', "_store${name}Unaligned");
}),
elementAtMethods = Map.fromIterable(optimizedTypes, value: (t) {
final name = nativeTypeClassNames[t];
return index.getTopLevelProcedure('dart:ffi', "_elementAt$name");
}),
loadAbiSpecificIntMethod =
index.getTopLevelProcedure('dart:ffi', "_loadAbiSpecificInt"),
loadAbiSpecificIntAtIndexMethod = index.getTopLevelProcedure(

View file

@ -123,7 +123,10 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
node.isExtensionMember &&
(node == allocationTearoff ||
node == asFunctionTearoff ||
node == lookupFunctionTearoff));
node == lookupFunctionTearoff ||
node == abiSpecificIntegerPointerElementAtTearoff ||
node == structPointerElementAtTearoff ||
node == unionPointerElementAtTearoff));
final result = super.visitProcedure(node);
_inFfiTearoff = false;
return result;
@ -201,6 +204,28 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
ensureNativeTypeValid(nativeType, node, allowCompounds: true);
return _replaceSetRef(node);
} else if (target == abiSpecificIntegerPointerElementAt ||
target == structPointerElementAt ||
target == unionPointerElementAt) {
final pointer = node.arguments.positional[0];
final index = node.arguments.positional[1];
final pointerType =
pointer.getStaticType(staticTypeContext!) as InterfaceType;
ensureNativeTypeValid(pointerType, pointer,
allowCompounds: true, allowInlineArray: true);
final DartType nativeType = node.arguments.types[0];
ensureNativeTypeValid(nativeType, node, allowCompounds: true);
Expression? inlineSizeOf = _inlineSizeOf(nativeType as InterfaceType);
if (inlineSizeOf != null) {
// Generates `receiver.offsetBy(inlineSizeOfExpression)`.
return InstanceInvocation(InstanceAccessKind.Instance, pointer,
offsetByMethod.name, Arguments([multiply(index, inlineSizeOf)]),
interfaceTarget: offsetByMethod,
functionType: Substitution.fromInterfaceType(pointerType)
.substituteType(offsetByMethod.getterType) as FunctionType);
}
} else if (target == structArrayElemAt || target == unionArrayElemAt) {
final DartType nativeType = node.arguments.types[0];
@ -704,60 +729,6 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
..fileOffset = node.fileOffset);
}
@override
visitInstanceInvocation(InstanceInvocation node) {
final modifiedExpression = _visitInstanceInvocation(node);
if (node == modifiedExpression) {
return super.visitInstanceInvocation(node);
}
// We've just created this node. We're likely not going to need to transform
// this node itself. Visit its sub expressions.
return super.defaultExpression(modifiedExpression);
}
/// Replaces nodes if they match. Does not invoke any super visit.
Expression _visitInstanceInvocation(InstanceInvocation node) {
if (_inFfiTearoff) {
return node;
}
final Member target = node.interfaceTarget;
try {
if (target == elementAtMethod) {
final DartType pointerType =
node.receiver.getStaticType(staticTypeContext!);
final DartType nativeType = _pointerTypeGetTypeArg(pointerType)!;
ensureNativeTypeValid(nativeType, node, allowCompounds: true);
Expression? inlineSizeOf = _inlineSizeOf(nativeType as InterfaceType);
if (inlineSizeOf != null) {
// Generates `receiver.offsetBy(inlineSizeOfExpression)`.
return InstanceInvocation(
InstanceAccessKind.Instance,
node.receiver,
offsetByMethod.name,
Arguments(
[multiply(node.arguments.positional.single, inlineSizeOf)]),
interfaceTarget: offsetByMethod,
functionType:
Substitution.fromInterfaceType(pointerType as InterfaceType)
.substituteType(offsetByMethod.getterType)
as FunctionType);
}
}
} on FfiStaticTypeError {
// It's OK to swallow the exception because the diagnostics issued will
// cause compilation to fail. By continuing, we can report more
// diagnostics before compilation ends.
}
return node;
}
DartType? _pointerTypeGetTypeArg(DartType pointerType) {
return pointerType is InterfaceType ? pointerType.typeArguments[0] : null;
}
void _ensureIsStaticFunction(Expression node) {
if ((node is StaticGet && node.target is Procedure) ||
(node is ConstantExpression &&

View file

@ -192,6 +192,9 @@ $platform$truncate$alignment external $dartType operator [](int index);
/// The $property at `address + ${sizeTimes}index`.
$platform$truncate$alignment external void operator []=(int index, $dartType value);
/// Pointer arithmetic (takes element size into account).
external Pointer<$nativeType> elementAt(int index);
$asTypedList
}
@ -237,7 +240,7 @@ void generatePatchExtension(
if (container == "Pointer") {
buffer.write("""
extension ${nativeType}Pointer on Pointer<$nativeType> {
@patch
@patch
$dartType get value => _load$nativeType(this, 0);
@patch
@ -249,6 +252,9 @@ extension ${nativeType}Pointer on Pointer<$nativeType> {
@patch
operator []=(int index, $dartType value) => _store$nativeType(this, ${sizeTimes}index, value);
@patch
Pointer<$nativeType> elementAt(int index) => Pointer.fromAddress(address + ${sizeTimes}index);
$asTypedList
}
@ -329,9 +335,12 @@ class Config {
final int elementSize;
final Version? since;
const Config(
this.nativeType, this.dartType, this.typedListType, this.elementSize,
{Version? since})
: since = since;
this.nativeType,
this.dartType,
this.typedListType,
this.elementSize, {
Version? since,
}) : since = since;
}
const String kDoNotEmit = "donotemit";

View file

@ -176,14 +176,6 @@ class Pointer<T extends NativeType> {
@pragma("vm:external-name", "Ffi_address")
external int get address;
// For statically known types, this is rewritten.
@patch
Pointer<T> elementAt(int index) {
// This case should have been rewritten in pre-processing.
// Only dynamic invocations are not rewritten in pre-processing.
throw UnsupportedError("Pointer.elementAt cannot be called dynamically.");
}
@patch
Pointer<T> _offsetBy(int offsetInBytes) =>
Pointer.fromAddress(address + offsetInBytes);
@ -447,43 +439,6 @@ bool _loadBool(Object typedDataBase, int offsetInBytes) =>
void _storeBool(Object typedDataBase, int offsetInBytes, bool value) =>
_storeUint8(typedDataBase, offsetInBytes, value ? 1 : 0);
Pointer<Bool> _elementAtBool(Pointer<Bool> pointer, int index) =>
Pointer.fromAddress(pointer.address + 1 * index);
Pointer<Int8> _elementAtInt8(Pointer<Int8> pointer, int index) =>
Pointer.fromAddress(pointer.address + 1 * index);
Pointer<Int16> _elementAtInt16(Pointer<Int16> pointer, int index) =>
Pointer.fromAddress(pointer.address + 2 * index);
Pointer<Int32> _elementAtInt32(Pointer<Int32> pointer, int index) =>
Pointer.fromAddress(pointer.address + 4 * index);
Pointer<Int64> _elementAtInt64(Pointer<Int64> pointer, int index) =>
Pointer.fromAddress(pointer.address + 8 * index);
Pointer<Uint8> _elementAtUint8(Pointer<Uint8> pointer, int index) =>
Pointer.fromAddress(pointer.address + 1 * index);
Pointer<Uint16> _elementAtUint16(Pointer<Uint16> pointer, int index) =>
Pointer.fromAddress(pointer.address + 2 * index);
Pointer<Uint32> _elementAtUint32(Pointer<Uint32> pointer, int index) =>
Pointer.fromAddress(pointer.address + 4 * index);
Pointer<Uint64> _elementAtUint64(Pointer<Uint64> pointer, int index) =>
Pointer.fromAddress(pointer.address + 8 * index);
Pointer<Float> _elementAtFloat(Pointer<Float> pointer, int index) =>
Pointer.fromAddress(pointer.address + 4 * index);
Pointer<Double> _elementAtDouble(Pointer<Double> pointer, int index) =>
Pointer.fromAddress(pointer.address + 8 * index);
Pointer<Pointer<S>> _elementAtPointer<S extends NativeType>(
Pointer<Pointer<S>> pointer, int index) =>
Pointer.fromAddress(pointer.address + _intPtrSize * index);
@pragma("vm:prefer-inline")
@pragma("vm:entry-point")
T _checkAbiSpecificIntegerMapping<T>(T? object) {
@ -520,6 +475,9 @@ extension Int8Pointer on Pointer<Int8> {
@patch
operator []=(int index, int value) => _storeInt8(this, index, value);
@patch
Pointer<Int8> elementAt(int index) => Pointer.fromAddress(address + index);
@patch
Int8List asTypedList(int length) {
ArgumentError.checkNotNull(this, "Pointer<Int8>");
@ -543,6 +501,10 @@ extension Int16Pointer on Pointer<Int16> {
@patch
operator []=(int index, int value) => _storeInt16(this, 2 * index, value);
@patch
Pointer<Int16> elementAt(int index) =>
Pointer.fromAddress(address + 2 * index);
@patch
Int16List asTypedList(int length) {
ArgumentError.checkNotNull(this, "Pointer<Int16>");
@ -566,6 +528,10 @@ extension Int32Pointer on Pointer<Int32> {
@patch
operator []=(int index, int value) => _storeInt32(this, 4 * index, value);
@patch
Pointer<Int32> elementAt(int index) =>
Pointer.fromAddress(address + 4 * index);
@patch
Int32List asTypedList(int length) {
ArgumentError.checkNotNull(this, "Pointer<Int32>");
@ -589,6 +555,10 @@ extension Int64Pointer on Pointer<Int64> {
@patch
operator []=(int index, int value) => _storeInt64(this, 8 * index, value);
@patch
Pointer<Int64> elementAt(int index) =>
Pointer.fromAddress(address + 8 * index);
@patch
Int64List asTypedList(int length) {
ArgumentError.checkNotNull(this, "Pointer<Int64>");
@ -612,6 +582,9 @@ extension Uint8Pointer on Pointer<Uint8> {
@patch
operator []=(int index, int value) => _storeUint8(this, index, value);
@patch
Pointer<Uint8> elementAt(int index) => Pointer.fromAddress(address + index);
@patch
Uint8List asTypedList(int length) {
ArgumentError.checkNotNull(this, "Pointer<Uint8>");
@ -635,6 +608,10 @@ extension Uint16Pointer on Pointer<Uint16> {
@patch
operator []=(int index, int value) => _storeUint16(this, 2 * index, value);
@patch
Pointer<Uint16> elementAt(int index) =>
Pointer.fromAddress(address + 2 * index);
@patch
Uint16List asTypedList(int length) {
ArgumentError.checkNotNull(this, "Pointer<Uint16>");
@ -658,6 +635,10 @@ extension Uint32Pointer on Pointer<Uint32> {
@patch
operator []=(int index, int value) => _storeUint32(this, 4 * index, value);
@patch
Pointer<Uint32> elementAt(int index) =>
Pointer.fromAddress(address + 4 * index);
@patch
Uint32List asTypedList(int length) {
ArgumentError.checkNotNull(this, "Pointer<Uint32>");
@ -681,6 +662,10 @@ extension Uint64Pointer on Pointer<Uint64> {
@patch
operator []=(int index, int value) => _storeUint64(this, 8 * index, value);
@patch
Pointer<Uint64> elementAt(int index) =>
Pointer.fromAddress(address + 8 * index);
@patch
Uint64List asTypedList(int length) {
ArgumentError.checkNotNull(this, "Pointer<Uint64>");
@ -704,6 +689,10 @@ extension FloatPointer on Pointer<Float> {
@patch
operator []=(int index, double value) => _storeFloat(this, 4 * index, value);
@patch
Pointer<Float> elementAt(int index) =>
Pointer.fromAddress(address + 4 * index);
@patch
Float32List asTypedList(int length) {
ArgumentError.checkNotNull(this, "Pointer<Float>");
@ -727,6 +716,10 @@ extension DoublePointer on Pointer<Double> {
@patch
operator []=(int index, double value) => _storeDouble(this, 8 * index, value);
@patch
Pointer<Double> elementAt(int index) =>
Pointer.fromAddress(address + 8 * index);
@patch
Float64List asTypedList(int length) {
ArgumentError.checkNotNull(this, "Pointer<Double>");
@ -749,6 +742,9 @@ extension BoolPointer on Pointer<Bool> {
@patch
operator []=(int index, bool value) => _storeBool(this, index, value);
@patch
Pointer<Bool> elementAt(int index) => Pointer.fromAddress(address + index);
}
extension Int8Array on Array<Int8> {
@ -919,6 +915,10 @@ extension PointerPointer<T extends NativeType> on Pointer<Pointer<T>> {
@patch
Pointer<T> operator [](int index) => _loadPointer(this, _intPtrSize * index);
@patch
Pointer<Pointer<T>> elementAt(int index) =>
Pointer.fromAddress(address + _intPtrSize * index);
@patch
operator []=(int index, Pointer<T> value) =>
_storePointer(this, _intPtrSize * index, value);
@ -940,6 +940,10 @@ extension StructPointer<T extends Struct> on Pointer<T> {
@patch
void operator []=(int index, T value) =>
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
@patch
Pointer<T> elementAt(int index) =>
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
}
extension UnionPointer<T extends Union> on Pointer<T> {
@ -958,6 +962,10 @@ extension UnionPointer<T extends Union> on Pointer<T> {
@patch
void operator []=(int index, T value) =>
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
@patch
Pointer<T> elementAt(int index) =>
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
}
extension AbiSpecificIntegerPointer<T extends AbiSpecificInteger>
@ -977,6 +985,10 @@ extension AbiSpecificIntegerPointer<T extends AbiSpecificInteger>
@patch
void operator []=(int index, int value) =>
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
@patch
Pointer<T> elementAt(int index) =>
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
}
extension PointerArray<T extends NativeType> on Array<Pointer<T>> {

View file

@ -32,5 +32,4 @@ class Unsized {
/// This [NativeType] does not have predefined size.
///
/// Unsized NativeTypes do not support [sizeOf] because their size is unknown.
/// Consequently, [Pointer.elementAt] is not available.
const unsized = const Unsized();

View file

@ -67,14 +67,6 @@ class Pointer<T extends NativeType> extends NativeType {
/// On 32-bit systems, the upper 32-bits of the result are 0.
external int get address;
/// Pointer arithmetic (takes element size into account).
///
/// This method must be invoked with a compile-time constant [T].
///
/// Does not accept dynamic invocations -- where the type of the receiver is
/// [dynamic].
external Pointer<T> elementAt(int index);
/// Cast Pointer<T> to a Pointer<V>.
external Pointer<U> cast<U extends NativeType>();
@ -191,6 +183,9 @@ extension Int8Pointer on Pointer<Int8> {
/// being stored, and the 8-bit value is sign-extended when it is loaded.
external void operator []=(int index, int value);
/// Pointer arithmetic (takes element size into account).
external Pointer<Int8> elementAt(int index);
/// Creates a typed list view backed by memory in the address space.
///
/// The returned view will allow access to the memory range from [address]
@ -229,6 +224,9 @@ extension Int16Pointer on Pointer<Int16> {
/// The [address] must be 2-byte aligned.
external void operator []=(int index, int value);
/// Pointer arithmetic (takes element size into account).
external Pointer<Int16> elementAt(int index);
/// Creates a typed list view backed by memory in the address space.
///
/// The returned view will allow access to the memory range from [address]
@ -269,6 +267,9 @@ extension Int32Pointer on Pointer<Int32> {
/// The [address] must be 4-byte aligned.
external void operator []=(int index, int value);
/// Pointer arithmetic (takes element size into account).
external Pointer<Int32> elementAt(int index);
/// Creates a typed list view backed by memory in the address space.
///
/// The returned view will allow access to the memory range from [address]
@ -300,6 +301,9 @@ extension Int64Pointer on Pointer<Int64> {
/// The [address] must be 8-byte aligned.
external void operator []=(int index, int value);
/// Pointer arithmetic (takes element size into account).
external Pointer<Int64> elementAt(int index);
/// Creates a typed list view backed by memory in the address space.
///
/// The returned view will allow access to the memory range from [address]
@ -334,6 +338,9 @@ extension Uint8Pointer on Pointer<Uint8> {
/// being stored, and the 8-bit value is zero-extended when it is loaded.
external void operator []=(int index, int value);
/// Pointer arithmetic (takes element size into account).
external Pointer<Uint8> elementAt(int index);
/// Creates a typed list view backed by memory in the address space.
///
/// The returned view will allow access to the memory range from [address]
@ -372,6 +379,9 @@ extension Uint16Pointer on Pointer<Uint16> {
/// The [address] must be 2-byte aligned.
external void operator []=(int index, int value);
/// Pointer arithmetic (takes element size into account).
external Pointer<Uint16> elementAt(int index);
/// Creates a typed list view backed by memory in the address space.
///
/// The returned view will allow access to the memory range from [address]
@ -412,6 +422,9 @@ extension Uint32Pointer on Pointer<Uint32> {
/// The [address] must be 4-byte aligned.
external void operator []=(int index, int value);
/// Pointer arithmetic (takes element size into account).
external Pointer<Uint32> elementAt(int index);
/// Creates a typed list view backed by memory in the address space.
///
/// The returned view will allow access to the memory range from [address]
@ -443,6 +456,9 @@ extension Uint64Pointer on Pointer<Uint64> {
/// The [address] must be 8-byte aligned.
external void operator []=(int index, int value);
/// Pointer arithmetic (takes element size into account).
external Pointer<Uint64> elementAt(int index);
/// Creates a typed list view backed by memory in the address space.
///
/// The returned view will allow access to the memory range from [address]
@ -483,6 +499,9 @@ extension FloatPointer on Pointer<Float> {
/// The [address] must be 4-byte aligned.
external void operator []=(int index, double value);
/// Pointer arithmetic (takes element size into account).
external Pointer<Float> elementAt(int index);
/// Creates a typed list view backed by memory in the address space.
///
/// The returned view will allow access to the memory range from [address]
@ -514,6 +533,9 @@ extension DoublePointer on Pointer<Double> {
/// The [address] must be 8-byte aligned.
external void operator []=(int index, double value);
/// Pointer arithmetic (takes element size into account).
external Pointer<Double> elementAt(int index);
/// Creates a typed list view backed by memory in the address space.
///
/// The returned view will allow access to the memory range from [address]
@ -539,6 +561,9 @@ extension BoolPointer on Pointer<Bool> {
/// The bool at `address + index`.
external void operator []=(int index, bool value);
/// Pointer arithmetic (takes element size into account).
external Pointer<Bool> elementAt(int index);
}
/// Bounds checking indexing methods on [Array]s of [Int8].
@ -663,6 +688,9 @@ extension PointerPointer<T extends NativeType> on Pointer<Pointer<T>> {
/// On 32-bit platforms the [address] must be 4-byte aligned, and on 64-bit
/// platforms the [address] must be 8-byte aligned.
external void operator []=(int index, Pointer<T> value);
/// Pointer arithmetic (takes element size into account).
external Pointer<Pointer<T>> elementAt(int index);
}
/// Extension on [Pointer] specialized for the type argument [Struct].
@ -699,6 +727,9 @@ extension StructPointer<T extends Struct> on Pointer<T> {
/// This extension method must be invoked on a receiver of type `Pointer<T>`
/// where `T` is a compile-time constant type.
external void operator []=(int index, T value);
/// Pointer arithmetic (takes element size into account).
external Pointer<T> elementAt(int index);
}
/// Extension on [Pointer] specialized for the type argument [Union].
@ -735,6 +766,9 @@ extension UnionPointer<T extends Union> on Pointer<T> {
/// This extension method must be invoked on a receiver of type `Pointer<T>`
/// where `T` is a compile-time constant type.
external void operator []=(int index, T value);
/// Pointer arithmetic (takes element size into account).
external Pointer<T> elementAt(int index);
}
/// Extension on [Pointer] specialized for the type argument
@ -752,6 +786,9 @@ extension AbiSpecificIntegerPointer<T extends AbiSpecificInteger>
/// The integer at `address + sizeOf<T>() * index`.
external void operator []=(int index, int value);
/// Pointer arithmetic (takes element size into account).
external Pointer<T> elementAt(int index);
}
/// Bounds checking indexing methods on [Array]s of [Pointer].