From 9609a10e09a480477f4b9a22ead9179dd58c8a05 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Mon, 18 Sep 2017 17:30:00 +0300 Subject: [PATCH] Improve StableObjPtr * Rename to StableRef * Make it more typed * Provide more idiomatic and convenient methods * Share more code between JVM and Native --- .../kotlin/native/interop/indexer/Utils.kt | 31 ++++---- .../kotlin/kotlinx/cinterop/JvmCallbacks.kt | 46 ++--------- .../main/kotlin/kotlinx/cinterop/StableRef.kt | 77 +++++++++++++++++++ .../kotlinx/cinterop/NativeCallbacks.kt | 47 ----------- .../kotlinx/cinterop/NativeStableRef.kt | 27 +++++++ .../src/main/kotlin/org/konan/libcurl/CUrl.kt | 10 +-- 6 files changed, 130 insertions(+), 108 deletions(-) create mode 100644 Interop/Runtime/src/main/kotlin/kotlinx/cinterop/StableRef.kt delete mode 100644 Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeCallbacks.kt create mode 100644 Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeStableRef.kt diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt index ea8f8f93aba..07eeaaf2926 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt @@ -138,16 +138,15 @@ internal fun CXTranslationUnit.ensureNoCompileErrors(): CXTranslationUnit { internal typealias CursorVisitor = (cursor: CValue, parent: CValue) -> CXChildVisitResult internal fun visitChildren(parent: CValue, visitor: CursorVisitor) { - val visitorPtr = StableObjPtr.create(visitor) + val visitorStableRef = StableObjPtr.create(visitor) try { - val clientData = visitorPtr.value + val clientData = visitorStableRef.asCPointer() clang_visitChildren(parent, staticCFunction { cursorIt, parentIt, clientDataIt -> - @Suppress("UNCHECKED_CAST") - val visitorIt = StableObjPtr.fromValue(clientDataIt!!).get() as CursorVisitor + val visitorIt = clientDataIt!!.asStableRef().get() visitorIt(cursorIt, parentIt) }, clientData) } finally { - visitorPtr.dispose() + visitorStableRef.dispose() } } @@ -156,19 +155,19 @@ internal fun visitChildren(translationUnit: CXTranslationUnit, visitor: CursorVi internal fun getFields(type: CValue): List> { val result = mutableListOf>() - val resultPtr = StableObjPtr.create(result) + val resultStableRef = StableRef.create(result) try { - val clientData = resultPtr.value + val clientData = resultStableRef.asCPointer() - @Suppress("NAME_SHADOWING", "UNCHECKED_CAST") + @Suppress("NAME_SHADOWING") clang_Type_visitFields(type, staticCFunction { cursor, clientData -> - val result = StableObjPtr.fromValue(clientData!!).get() as MutableList> + val result = clientData!!.asStableRef>>().get() result.add(cursor) CXVisitorResult.CXVisit_Continue }, clientData) } finally { - resultPtr.dispose() + resultStableRef.dispose() } return result @@ -340,16 +339,16 @@ internal interface Indexer { } internal fun indexTranslationUnit(index: CXIndex, translationUnit: CXTranslationUnit, options: Int, indexer: Indexer) { - val indexerStablePtr = StableObjPtr.create(indexer) + val indexerStableRef = StableObjPtr.create(indexer) try { - val clientData = indexerStablePtr.value + val clientData = indexerStableRef.asCPointer() memScoped { val indexerCallbacks = alloc().apply { abortQuery = null diagnostic = null enteredMainFile = staticCFunction { clientData, mainFile, _ -> @Suppress("NAME_SHADOWING") - val indexer = StableObjPtr.fromValue(clientData!!).get() as Indexer + val indexer = clientData!!.asStableRef().get() indexer.enteredMainFile(mainFile!!) // We must ensure only interop types exist in function signature. @Suppress("USELESS_CAST") @@ -357,7 +356,7 @@ internal fun indexTranslationUnit(index: CXIndex, translationUnit: CXTranslation } ppIncludedFile = staticCFunction { clientData, info -> @Suppress("NAME_SHADOWING") - val indexer = StableObjPtr.fromValue(clientData!!).get() as Indexer + val indexer = clientData!!.asStableRef().get() indexer.ppIncludedFile(info!!.pointed) // We must ensure only interop types exist in function signature. @Suppress("USELESS_CAST") @@ -367,7 +366,7 @@ internal fun indexTranslationUnit(index: CXIndex, translationUnit: CXTranslation startedTranslationUnit = null indexDeclaration = staticCFunction { clientData, info -> @Suppress("NAME_SHADOWING") - val nativeIndex = StableObjPtr.fromValue(clientData!!).get() as Indexer + val nativeIndex = clientData!!.asStableRef().get() nativeIndex.indexDeclaration(info!!.pointed) } indexEntityReference = null @@ -386,7 +385,7 @@ internal fun indexTranslationUnit(index: CXIndex, translationUnit: CXTranslation } } } finally { - indexerStablePtr.dispose() + indexerStableRef.dispose() } } diff --git a/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmCallbacks.kt b/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmCallbacks.kt index dbb96fac255..71ece4c1be4 100644 --- a/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmCallbacks.kt +++ b/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmCallbacks.kt @@ -25,48 +25,12 @@ import kotlin.reflect.full.declaredMemberProperties import kotlin.reflect.full.isSubclassOf import kotlin.reflect.jvm.reflect -/** - * This class provides a way to create a stable handle to any Kotlin object. - * Its [value] can be safely passed to native code e.g. to be received in a Kotlin callback. - * - * Any [StableObjPtr] should be manually [disposed][dispose] - */ -data class StableObjPtr private constructor(val value: COpaquePointer) { +internal fun createStablePointer(any: Any): COpaquePointer = newGlobalRef(any).toCPointer()!! - companion object { +internal fun disposeStablePointer(pointer: COpaquePointer) = deleteGlobalRef(pointer.toLong()) - /** - * Creates a handle for given object. - */ - fun create(any: Any) = fromValue(newGlobalRef(any)) - - private fun fromValue(value: NativePtr) = fromValue(interpretCPointer(value)!!) - - /** - * Creates [StableObjPtr] from given raw value. - * - * @param value must be a [value] of some [StableObjPtr] - */ - fun fromValue(value: COpaquePointer) = StableObjPtr(value) - - init { - loadCallbacksLibrary() - } - } - - /** - * Disposes the handle. It must not be [used][get] after that. - */ - fun dispose() { - deleteGlobalRef(value.rawValue) - } - - /** - * Returns the object this handle was [created][create] for. - */ - fun get(): Any = derefGlobalRef(value.rawValue) - -} +@PublishedApi +internal fun derefStablePointer(pointer: COpaquePointer): Any = derefGlobalRef(pointer.toLong()) private fun getFieldCType(type: KType): CType<*> { val classifier = type.classifier @@ -400,6 +364,8 @@ private fun loadCallbacksLibrary() { System.loadLibrary("callbacks") } +private val topLevelInitializer = loadCallbacksLibrary() + /** * Reference to `ffi_type` struct instance. diff --git a/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/StableRef.kt b/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/StableRef.kt new file mode 100644 index 00000000000..76df8b9767f --- /dev/null +++ b/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/StableRef.kt @@ -0,0 +1,77 @@ +/* + * 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 + +@Deprecated("Use StableRef instead", ReplaceWith("StableRef")) +typealias StableObjPtr = StableRef<*> + +/** + * This class provides a way to create a stable handle to any Kotlin object. + * After [converting to CPointer][asCPointer] it can be safely passed to native code e.g. to be received + * in a Kotlin callback. + * + * Any [StableRef] should be manually [disposed][dispose] + */ +data class StableRef @PublishedApi internal constructor( + private val stablePtr: COpaquePointer, + private val ref: T // Note: storing the reference itself to avoid unchecked casts. +) { + + companion object { + + /** + * Creates a handle for given object. + */ + fun create(any: T) = StableRef(createStablePointer(any), any) + + /** + * Creates [StableRef] from given raw value. + * + * @param value must be a [value] of some [StableRef] + */ + @Deprecated("Use CPointer<*>.asStableRef() instead", ReplaceWith("ptr.asStableRef()")) + fun fromValue(value: COpaquePointer) = value.asStableRef() + } + + @Deprecated("Use .asCPointer() instead", ReplaceWith("this.asCPointer()")) + val value: COpaquePointer get() = this.asCPointer() + + /** + * Converts the handle to C pointer. + * @see [asStableRef] + */ + fun asCPointer(): COpaquePointer = this.stablePtr + + /** + * Disposes the handle. It must not be used after that. + */ + fun dispose() { + disposeStablePointer(this.stablePtr) + } + + /** + * Returns the object this handle was [created][StableRef.create] for. + */ + fun get() = this.ref + +} + +/** + * Converts to [StableRef] this opaque pointer produced by [StableRef.asCPointer]. + */ +inline fun CPointer<*>.asStableRef() = + StableRef(this, derefStablePointer(this) as T) diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeCallbacks.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeCallbacks.kt deleted file mode 100644 index e99f2754c72..00000000000 --- a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeCallbacks.kt +++ /dev/null @@ -1,47 +0,0 @@ -package kotlinx.cinterop - -/** - * This class provides a way to create a stable handle to any Kotlin object. - * Its [value] can be safely passed to native code e.g. to be received in a Kotlin callback. - * - * Any [StableObjPtr] should be manually [disposed][dispose] - */ -data class StableObjPtr private constructor(val value: COpaquePointer) { - - companion object { - - /** - * Creates a handle for given object. - */ - fun create(any: Any) = fromValue(createStablePointer(any)) - - /** - * Creates [StableObjPtr] from given raw value. - * - * @param value must be a [value] of some [StableObjPtr] - */ - fun fromValue(value: COpaquePointer) = StableObjPtr(value) - } - - /** - * Disposes the handle. It must not be [used][get] after that. - */ - fun dispose() { - disposeStablePointer(value) - } - - /** - * Returns the object this handle was [created][create] for. - */ - fun get(): Any = derefStablePointer(value) - -} - -@SymbolName("Kotlin_Interop_createStablePointer") -private external fun createStablePointer(any: Any): COpaquePointer - -@SymbolName("Kotlin_Interop_disposeStablePointer") -private external fun disposeStablePointer(pointer: COpaquePointer) - -@SymbolName("Kotlin_Interop_derefStablePointer") -private external fun derefStablePointer(pointer: COpaquePointer): Any diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeStableRef.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeStableRef.kt new file mode 100644 index 00000000000..bc38cc54535 --- /dev/null +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeStableRef.kt @@ -0,0 +1,27 @@ +/* + * 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 + +@SymbolName("Kotlin_Interop_createStablePointer") +internal external fun createStablePointer(any: Any): COpaquePointer + +@SymbolName("Kotlin_Interop_disposeStablePointer") +internal external fun disposeStablePointer(pointer: COpaquePointer) + +@PublishedApi +@SymbolName("Kotlin_Interop_derefStablePointer") +internal external fun derefStablePointer(pointer: COpaquePointer): Any diff --git a/samples/libcurl/src/main/kotlin/org/konan/libcurl/CUrl.kt b/samples/libcurl/src/main/kotlin/org/konan/libcurl/CUrl.kt index 6da4b377fd3..309929d1778 100644 --- a/samples/libcurl/src/main/kotlin/org/konan/libcurl/CUrl.kt +++ b/samples/libcurl/src/main/kotlin/org/konan/libcurl/CUrl.kt @@ -4,7 +4,7 @@ import kotlinx.cinterop.* import libcurl.* class CUrl(val url: String) { - val stablePtr = StableObjPtr.create(this) + val stableRef = StableRef.create(this) val curl = curl_easy_init(); @@ -12,7 +12,7 @@ class CUrl(val url: String) { curl_easy_setopt(curl, CURLOPT_URL, url) val header = staticCFunction(::header_callback) curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header) - curl_easy_setopt(curl, CURLOPT_HEADERDATA, stablePtr.value) + curl_easy_setopt(curl, CURLOPT_HEADERDATA, stableRef.asCPointer()) } val header = Event() @@ -26,7 +26,7 @@ class CUrl(val url: String) { fun close() { curl_easy_cleanup(curl) - stablePtr.dispose() + stableRef.dispose() } } @@ -39,7 +39,7 @@ fun header_callback(buffer: CPointer?, size: size_t, nitems: size_t, us if (buffer == null) return 0 val header = buffer.toKString((size * nitems).toInt()).trim() if (userdata != null) { - val curl = StableObjPtr.fromValue(userdata).get() as CUrl + val curl = userdata.asStableRef().get() curl.header(header) } return size * nitems @@ -49,7 +49,7 @@ fun header_callback(buffer: CPointer?, size: size_t, nitems: size_t, us fun write_callback(buffer: COpaquePointer?, size: size_t, nitems: size_t, userdata: COpaquePointer?): size_t { if (buffer == null) return 0 if (userdata != null) { - val curl = StableObjPtr.fromValue(userdata).get() as CUrl + val curl = userdata.asStableRef().get() curl.data(buffer.) } return size * nitems