Improve StableObjPtr

* Rename to StableRef
* Make it more typed
* Provide more idiomatic and convenient methods
* Share more code between JVM and Native
This commit is contained in:
Svyatoslav Scherbina
2017-09-18 17:30:00 +03:00
committed by SvyatoslavScherbina
parent d8ed1f4414
commit 9609a10e09
6 changed files with 130 additions and 108 deletions
@@ -138,16 +138,15 @@ internal fun CXTranslationUnit.ensureNoCompileErrors(): CXTranslationUnit {
internal typealias CursorVisitor = (cursor: CValue<CXCursor>, parent: CValue<CXCursor>) -> CXChildVisitResult
internal fun visitChildren(parent: CValue<CXCursor>, 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<CursorVisitor>().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<CXType>): List<CValue<CXCursor>> {
val result = mutableListOf<CValue<CXCursor>>()
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<CValue<CXCursor>>
val result = clientData!!.asStableRef<MutableList<CValue<CXCursor>>>().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<IndexerCallbacks>().apply {
abortQuery = null
diagnostic = null
enteredMainFile = staticCFunction { clientData, mainFile, _ ->
@Suppress("NAME_SHADOWING")
val indexer = StableObjPtr.fromValue(clientData!!).get() as Indexer
val indexer = clientData!!.asStableRef<Indexer>().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<Indexer>().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<Indexer>().get()
nativeIndex.indexDeclaration(info!!.pointed)
}
indexEntityReference = null
@@ -386,7 +385,7 @@ internal fun indexTranslationUnit(index: CXIndex, translationUnit: CXTranslation
}
}
} finally {
indexerStablePtr.dispose()
indexerStableRef.dispose()
}
}
@@ -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.
@@ -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<T> instead", ReplaceWith("StableRef<T>"))
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<out T : Any> @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 <T : Any> create(any: T) = StableRef<T>(createStablePointer(any), any)
/**
* Creates [StableRef] from given raw value.
*
* @param value must be a [value] of some [StableRef]
*/
@Deprecated("Use CPointer<*>.asStableRef<T>() instead", ReplaceWith("ptr.asStableRef<T>()"))
fun fromValue(value: COpaquePointer) = value.asStableRef<Any>()
}
@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 <reified T : Any> CPointer<*>.asStableRef() =
StableRef<T>(this, derefStablePointer(this) as T)
@@ -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
@@ -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
@@ -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<String>()
@@ -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<ByteVar>?, 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<CUrl>().get()
curl.header(header)
}
return size * nitems
@@ -49,7 +49,7 @@ fun header_callback(buffer: CPointer<ByteVar>?, 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<CUrl>().get()
curl.data(buffer.)
}
return size * nitems