diff --git a/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Types.kt b/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Types.kt index b2d762d8bc1..c3aec59c927 100644 --- a/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Types.kt +++ b/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Types.kt @@ -64,6 +64,8 @@ abstract class CPointed(rawPtr: NativePtr) : NativePointed(rawPtr) * Passing [CValues] has nearly the same semantics as passing by value: the C function receives * the pointer to the temporary copy of these values, and the caller can't observe the modifications to this copy. * The copy is valid until the C function returns. + * There are also other implementations of [CValuesRef] that provide temporary pointer, + * e.g. Kotlin Native specific [refTo] functions to pass primitive arrays directly to native. */ abstract class CValuesRef { /** diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Pinning.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Pinning.kt new file mode 100644 index 00000000000..05e40ece05b --- /dev/null +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Pinning.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlinx.cinterop + +data class Pinned internal constructor(private val stablePtr: COpaquePointer) { + + /** + * Disposes the handle. It must not be [used][get] after that. + */ + fun unpin() { + disposeStablePointer(this.stablePtr) + } + + /** + * Returns the underlying pinned object. + */ + fun get(): T = @Suppress("UNCHECKED_CAST") (derefStablePointer(stablePtr) as T) + +} + +fun T.pin() = Pinned(createStablePointer(this)) + +inline fun T.usePinned(block: (Pinned) -> R): R { + val pinned = this.pin() + return try { + block(pinned) + } finally { + pinned.unpin() + } +} + +fun Pinned.addressOf(index: Int): CPointer = this.addressOfElement(index) +fun ByteArray.refTo(index: Int): CValuesRef = this.usingPinned { addressOf(index) } + +fun Pinned.addressOf(index: Int): CPointer = this.addressOfElement(index) +fun ShortArray.refTo(index: Int): CValuesRef = this.usingPinned { addressOf(index) } + +fun Pinned.addressOf(index: Int): CPointer = this.addressOfElement(index) +fun IntArray.refTo(index: Int): CValuesRef = this.usingPinned { addressOf(index) } + +fun Pinned.addressOf(index: Int): CPointer = this.addressOfElement(index) +fun LongArray.refTo(index: Int): CValuesRef = this.usingPinned { addressOf(index) } + +fun Pinned.addressOf(index: Int): CPointer = this.addressOfElement(index) +fun FloatArray.refTo(index: Int): CValuesRef = this.usingPinned { addressOf(index) } + +fun Pinned.addressOf(index: Int): CPointer = this.addressOfElement(index) +fun DoubleArray.refTo(index: Int): CValuesRef = this.usingPinned { addressOf(index) } + +private inline fun T.usingPinned( + crossinline block: Pinned.() -> CPointer

+) = object : CValuesRef

() { + + override fun getPointer(scope: AutofreeScope): CPointer

{ + val pinned = this@usingPinned.pin() + scope.defer { pinned.unpin() } + return pinned.block() + } +} + +@SymbolName("Kotlin_Arrays_getAddressOfElement") +private external fun getAddressOfElement(array: Any, index: Int): COpaquePointer + +@Suppress("NOTHING_TO_INLINE") +private inline fun

Pinned<*>.addressOfElement(index: Int): CPointer

= + getAddressOfElement(this.get(), index).reinterpret() diff --git a/backend.native/tests/interop/basics/3.kt b/backend.native/tests/interop/basics/3.kt index 1d957758697..5ed1ba182cc 100644 --- a/backend.native/tests/interop/basics/3.kt +++ b/backend.native/tests/interop/basics/3.kt @@ -1,27 +1,19 @@ import kotlinx.cinterop.* fun main(args: Array) { - memScoped { - val count = 5 + val values = intArrayOf(14, 12, 9, 13, 8) + val count = values.size - val values = allocArray(count) - values[0] = 14 - values[1] = 12 - values[2] = 9 - values[3] = 13 - values[4] = 8 + cstdlib.qsort(values.refTo(0), count.toLong(), IntVar.size, staticCFunction { a, b -> + val aValue = a!!.reinterpret()[0] + val bValue = b!!.reinterpret()[0] - cstdlib.qsort(values, count.toLong(), IntVar.size, staticCFunction { a, b -> - val aValue = a!!.reinterpret()[0] - val bValue = b!!.reinterpret()[0] + (aValue - bValue) + }) - (aValue - bValue) - }) - - for (i in 0 .. count - 1) { - print(values[i]) - print(" ") - } - println() + for (i in 0..count - 1) { + print(values[i]) + print(" ") } + println() } diff --git a/runtime/src/main/cpp/Arrays.cpp b/runtime/src/main/cpp/Arrays.cpp index 913e573ea44..8c4c432ef54 100644 --- a/runtime/src/main/cpp/Arrays.cpp +++ b/runtime/src/main/cpp/Arrays.cpp @@ -350,4 +350,13 @@ KNativePtr Kotlin_ImmutableBinaryBlob_asCPointerImpl(KRef thiz, KInt offset) { return PrimitiveArrayAddressOfElementAt(array, offset); } +KNativePtr Kotlin_Arrays_getAddressOfElement(KRef thiz, KInt index) { + ArrayHeader* array = thiz->array(); + if (index < 0 || index >= array->count_) { + ThrowArrayIndexOutOfBoundsException(); + } + + return AddressOfElementAt(array, index); +} + } // extern "C"