// 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$ extends JType> { _$JArray$Type$(JType elementType) : signature = '[${elementType.signature}'; @override final String signature; } extension type JArray._(JObject _$this) implements JObject { /// The type which includes information such as the signature of this class. static JType> 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); } /// Copies the elements of [iterable], skipping [skipCount] elements first, /// into this array in the range [start] to [end]. void setRange(int start, int end, Iterable 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 with ListMixin, NonGrowableListMixin { final JArray _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 on JArray { /// Returns a [List] view into this array. /// /// Any changes to this list will reflect in the original array as well. List asDart() => _JArrayListView(this); } void _allocate( int byteCount, void Function(Pointer ptr) use, ) { using((arena) { final ptr = arena.allocate(byteCount); use(ptr); }, malloc); } extension on Allocator { Pointer? get _nativeFree { return switch (this) { malloc => malloc.nativeFree, calloc => calloc.nativeFree, _ => null, }; } }