| // Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'dart:collection'; | |
| import 'dart:ffi'; | |
| import 'dart:typed_data'; | |
| import 'package:collection/collection.dart'; | |
| import 'package:ffi/ffi.dart'; | |
| import 'jni.dart'; | |
| import 'jobject.dart'; | |
| import 'jreference.dart'; | |
| import 'types.dart'; | |
| part 'primitive_jarrays.dart'; | |
| final class _$JArray$Type$<E extends JObject?> extends JType<JArray<E>> { | |
| _$JArray$Type$(JType<E> elementType) | |
| : signature = '[${elementType.signature}'; | |
| @override | |
| final String signature; | |
| } | |
| extension type JArray<E extends JObject?>._(JObject _$this) implements JObject { | |
| /// The type which includes information such as the signature of this class. | |
| static JType<JArray<$E>> type<$E extends JObject?>(JType<$E> innerType) => | |
| _$JArray$Type$<$E>(innerType); | |
| /// Creates a [JArray] of the given length from the given [elementType]. | |
| /// | |
| /// The [length] must be a non-negative integer. | |
| static JArray<$E?> withLength<$E extends JObject?>( | |
| JType<$E> elementType, int length) { | |
| RangeError.checkNotNegative(length); | |
| return _newArray<$E>(elementType.jClass, length); | |
| } | |
| static JArray<$E> _newArray<$E extends JObject?>(JClass jClass, int length, | |
| [$E? fill]) { | |
| final classRef = jClass.reference; | |
| final fillRef = fill?.reference ?? jNullReference; | |
| final array = JObject.fromReference( | |
| JGlobalReference(Jni.env.NewObjectArray( | |
| length, | |
| classRef.pointer, | |
| fillRef.pointer, | |
| )), | |
| ) as JArray<$E>; | |
| classRef.release(); | |
| return array; | |
| } | |
| /// Creates a [JArray] of the given length with [fill] at each position. | |
| /// | |
| /// The [length] must be a non-negative integer. | |
| static JArray<$E> filled<$E extends JObject>(int length, $E fill, | |
| {JType<$E>? elementType}) { | |
| RangeError.checkNotNegative(length); | |
| final jClass = elementType == null ? fill.jClass : elementType.jClass; | |
| return _newArray<$E>(jClass, length, fill); | |
| } | |
| /// Creates a [JArray] from `elements`. | |
| static JArray<$E> of<$E extends JObject?>( | |
| JType<$E> elementType, Iterable<$E> elements) { | |
| final len = elements.length; | |
| return _newArray<$E>(elementType.jClass, len)..setRange(0, len, elements); | |
| } | |
| /// The number of elements in this array. | |
| int get length => Jni.env.GetArrayLength(reference.pointer); | |
| E _elementAt(int index) { | |
| RangeError.checkValueInInterval(index, 0, length - 1); | |
| final pointer = Jni.env.GetObjectArrayElement(reference.pointer, index); | |
| if (pointer == nullptr) { | |
| return null as E; | |
| } | |
| return JObject.fromReference(JGlobalReference(pointer)) as E; | |
| } | |
| E operator [](int index) { | |
| return _elementAt(index); | |
| } | |
| void operator []=(int index, E value) { | |
| RangeError.checkValueInInterval(index, 0, length - 1); | |
| final valueRef = value?.reference ?? jNullReference; | |
| Jni.env.SetObjectArrayElement(reference.pointer, index, valueRef.pointer); | |
| } | |
| void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) { | |
| RangeError.checkValidRange(start, end, length); | |
| final rangeLength = end - start; | |
| final it = iterable.skip(skipCount).take(rangeLength); | |
| for (final (index, element) in it.indexed) { | |
| this[index] = element; | |
| } | |
| } | |
| } | |
| final class _JArrayListView<E extends JObject?> | |
| with ListMixin<E>, NonGrowableListMixin<E> { | |
| final JArray<E> _jarray; | |
| _JArrayListView(this._jarray); | |
| @override | |
| int get length => _jarray.length; | |
| @override | |
| E operator [](int index) { | |
| return _jarray[index]; | |
| } | |
| @override | |
| void operator []=(int index, E value) { | |
| _jarray[index] = value; | |
| } | |
| } | |
| extension JArrayToList<E extends JObject?> on JArray<E> { | |
| /// Returns a [List] view into this array. | |
| /// | |
| /// Any changes to this list will reflect in the original array as well. | |
| List<E> asDart() => _JArrayListView<E>(this); | |
| } | |
| void _allocate<T extends NativeType>( | |
| int byteCount, | |
| void Function(Pointer<T> ptr) use, | |
| ) { | |
| using((arena) { | |
| final ptr = arena.allocate<T>(byteCount); | |
| use(ptr); | |
| }, malloc); | |
| } | |
| extension on Allocator { | |
| Pointer<NativeFinalizerFunction>? get _nativeFree { | |
| return switch (this) { | |
| malloc => malloc.nativeFree, | |
| calloc => calloc.nativeFree, | |
| _ => null, | |
| }; | |
| } | |
| } |