Implement explicit and implicit object pinning for interop

This allows passing Kotlin primitive arrays directly to native
This commit is contained in:
Svyatoslav Scherbina
2017-09-19 10:08:07 +03:00
committed by SvyatoslavScherbina
parent fd35ccbf2e
commit a6ca64871f
4 changed files with 102 additions and 19 deletions
@@ -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<T : CPointed> {
/**
@@ -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<out T : Any> 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 : Any> T.pin() = Pinned<T>(createStablePointer(this))
inline fun <T : Any, R> T.usePinned(block: (Pinned<T>) -> R): R {
val pinned = this.pin()
return try {
block(pinned)
} finally {
pinned.unpin()
}
}
fun Pinned<ByteArray>.addressOf(index: Int): CPointer<ByteVar> = this.addressOfElement(index)
fun ByteArray.refTo(index: Int): CValuesRef<ByteVar> = this.usingPinned { addressOf(index) }
fun Pinned<ShortArray>.addressOf(index: Int): CPointer<ShortVar> = this.addressOfElement(index)
fun ShortArray.refTo(index: Int): CValuesRef<ShortVar> = this.usingPinned { addressOf(index) }
fun Pinned<IntArray>.addressOf(index: Int): CPointer<IntVar> = this.addressOfElement(index)
fun IntArray.refTo(index: Int): CValuesRef<IntVar> = this.usingPinned { addressOf(index) }
fun Pinned<LongArray>.addressOf(index: Int): CPointer<LongVar> = this.addressOfElement(index)
fun LongArray.refTo(index: Int): CValuesRef<LongVar> = this.usingPinned { addressOf(index) }
fun Pinned<FloatArray>.addressOf(index: Int): CPointer<FloatVar> = this.addressOfElement(index)
fun FloatArray.refTo(index: Int): CValuesRef<FloatVar> = this.usingPinned { addressOf(index) }
fun Pinned<DoubleArray>.addressOf(index: Int): CPointer<DoubleVar> = this.addressOfElement(index)
fun DoubleArray.refTo(index: Int): CValuesRef<DoubleVar> = this.usingPinned { addressOf(index) }
private inline fun <T : Any, P : CPointed> T.usingPinned(
crossinline block: Pinned<T>.() -> CPointer<P>
) = object : CValuesRef<P>() {
override fun getPointer(scope: AutofreeScope): CPointer<P> {
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 <P : CVariable> Pinned<*>.addressOfElement(index: Int): CPointer<P> =
getAddressOfElement(this.get(), index).reinterpret()
+11 -19
View File
@@ -1,27 +1,19 @@
import kotlinx.cinterop.*
fun main(args: Array<String>) {
memScoped {
val count = 5
val values = intArrayOf(14, 12, 9, 13, 8)
val count = values.size
val values = allocArray<IntVar>(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<IntVar>()[0]
val bValue = b!!.reinterpret<IntVar>()[0]
cstdlib.qsort(values, count.toLong(), IntVar.size, staticCFunction { a, b ->
val aValue = a!!.reinterpret<IntVar>()[0]
val bValue = b!!.reinterpret<IntVar>()[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()
}
+9
View File
@@ -350,4 +350,13 @@ KNativePtr Kotlin_ImmutableBinaryBlob_asCPointerImpl(KRef thiz, KInt offset) {
return PrimitiveArrayAddressOfElementAt<KByte>(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"