Interop: remove old version

This commit is contained in:
Svyatoslav Scherbina
2016-11-22 12:10:49 +07:00
parent 183f6a6d2f
commit a58d682d40
4 changed files with 0 additions and 611 deletions
@@ -1,46 +0,0 @@
package kotlin_native.interop
import sun.misc.Unsafe
data class NativePtr private constructor(internal val value: Long) {
fun displacedBy(offset: Int) = NativePtr(value + offset)
companion object {
fun byValue(value: Long): NativePtr? {
return if (value == 0L) {
null
} else {
NativePtr(value)
}
}
}
}
fun NativePtr?.asLong() = if (this == null) 0L else value
internal object bridge {
fun malloc(size: Int): NativePtr = NativePtr.byValue(unsafe.allocateMemory(size.toLong()))!!
fun free(ptr: NativePtr) = unsafe.freeMemory(ptr.asLong())
fun getInt64(ptr: NativePtr): Long = unsafe.getLong(ptr.asLong())
fun putInt64(ptr: NativePtr, value: Long) = unsafe.putLong(ptr.asLong(), value)
fun getPtr(ptr: NativePtr): NativePtr? = NativePtr.byValue(unsafe.getLong(ptr.asLong()))
fun putPtr(ptr: NativePtr, value: NativePtr?) = unsafe.putLong(ptr.asLong(), value.asLong())
fun getInt32(ptr: NativePtr): Int = unsafe.getInt(ptr.asLong())
fun putInt32(ptr: NativePtr, value: Int) = unsafe.putInt(ptr.asLong(), value)
fun getInt16(ptr: NativePtr): Short = unsafe.getShort(ptr.asLong())
fun putInt16(ptr: NativePtr, value: Short) = unsafe.putShort(ptr.asLong(), value)
fun getInt8(ptr: NativePtr): Byte = unsafe.getByte(ptr.asLong())
fun putInt8(ptr: NativePtr, value: Byte) = unsafe.putByte(ptr.asLong(), value)
private val unsafe = with(Unsafe::class.java.getDeclaredField("theUnsafe")) {
isAccessible = true
return@with this.get(null) as Unsafe
}
}
@@ -1,287 +0,0 @@
package kotlin_native.interop
/**
* 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: NativePtr) {
companion object {
/**
* Creates a handle for given object.
*/
fun create(any: Any) = StableObjPtr(NativePtr.byValue(newGlobalRef(any))!!)
/**
* Creates [StableObjPtr] from given raw value.
*
* @param value must be a [value] of some [StableObjPtr]
*/
fun fromValue(value: NativePtr) = StableObjPtr(value)
init {
loadCallbacksLibrary()
}
}
/**
* Disposes the handle. It must not be [used][get] after that.
*/
fun dispose() {
deleteGlobalRef(value.value)
}
/**
* Returns the object this handle was [created][create] for.
*/
fun get(): Any = derefGlobalRef(value.value)
}
/**
* Describes the type of native function.
*
* The instances of this class are supposed to be Kotlin objects (singletons),
* because creating the instance implies allocating some amount of non-freeable memory for the instance itself
* and for any unique Kotlin function "converted" to this type.
*
* Native function type definition consists in the following:
* - Definitions of native function's parameter and return types to be passed into the constructor
* - Implementation of [invoke] method which describes how to convert between these types and Kotlin types used in [F]
*
* @param F Kotlin function type corresponding to given native function type
*/
abstract class NativeFunctionType<F : Function<*>> protected constructor(returnType: CType, vararg paramTypes: CType) {
/**
* Returns a native function of this type, which calls given Kotlin *static* function.
*
* Given function must be *static*, i.e. an (unbound) reference to a Kotlin function or
* a closure which doesn't capture any variable
*/
fun fromStatic(function: F): NativePtr {
// TODO: optimize synchronization
synchronized(cache) {
return cache.getOrPut(function, { createFromStatic(function) })
}
}
/**
* Describes the C type of a function's parameter or return value.
* It is supposed to be constructed using the primitive types (such as [SInt32]) and the [Struct] combinator.
*
* This description omits the details that are irrelevant for the ABI.
*/
protected open class CType internal constructor(val ffiType: ffi_type) {
internal constructor(ffiTypePtr: Long) : this(NativePtr.byValue(ffiTypePtr).asRef(ffi_type)!!)
}
protected object Void : CType(ffiTypeVoid())
protected object UInt8 : CType(ffiTypeUInt8())
protected object SInt8 : CType(ffiTypeSInt8())
protected object UInt16 : CType(ffiTypeUInt16())
protected object SInt16 : CType(ffiTypeSInt16())
protected object UInt32 : CType(ffiTypeUInt32())
protected object SInt32 : CType(ffiTypeSInt32())
protected object UInt64 : CType(ffiTypeUInt64())
protected object SInt64 : CType(ffiTypeSInt64())
protected object Pointer : CType(ffiTypePointer())
protected class Struct(vararg elementTypes: CType) : CType(
ffiTypeStruct(
elementTypes.map { it.ffiType }
)
)
/**
* This method should invoke given Kotlin function.
*
* @param args array of pointers to arguments to be passed to [function]
* @param ret pointer to memory to be filled with return value of [function]
*/
protected abstract fun invoke(function: F, args: NativeArray<NativePtrBox>, ret: NativePtr)
companion object {
init {
loadCallbacksLibrary()
}
}
private val ffiCif = ffiCreateCif(returnType.ffiType, paramTypes.map { it.ffiType })
/**
* Allocates a native function of this type for given Kotlin function.
*/
private fun createFromStatic(function: F): NativePtr {
if (!isStatic(function)) {
throw IllegalArgumentException()
}
val impl = { ret: NativePtr, args: NativeArray<NativePtrBox> ->
invoke(function, args, ret)
}
return ffiCreateClosure(ffiCif, impl)
}
/**
* Returns `true` if given function is *static* as defined in [fromStatic].
*/
private fun isStatic(function: Function<*>): Boolean {
// TODO: revise
try {
with(function.javaClass.getDeclaredField("INSTANCE")) {
if (!java.lang.reflect.Modifier.isStatic(modifiers) || !java.lang.reflect.Modifier.isFinal(modifiers)) {
return false
}
isAccessible = true // TODO: undo
return get(null) == function
}
} catch (e: NoSuchFieldException) {
return false
}
}
private val cache = mutableMapOf<F, NativePtr>()
}
/**
* @see NativeFunctionType.fromStatic
*/
fun <F : Function<*>> F.staticAsNative(type: NativeFunctionType<F>) = type.fromStatic(this)
/**
* Describes a "struct" with native function pointer field.
*/
class NativeFunctionBox<F : Function<*>>(ptr: NativePtr, private val type: NativeFunctionType<F>) : NativeRef(ptr) {
/**
* Sets the function pointer field to null or native function calling given Kotlin function.
*/
fun setStatic(function: F?) {
val nativeFunPtr = function?.staticAsNative(type)
bridge.putPtr(ptr, nativeFunPtr)
}
}
val <F : Function<*>> NativeFunctionType<F>.ref: NativeRef.TypeWithSize<NativeFunctionBox<F>>
get() = NativeRef.TypeWithSize(8, { NativeFunctionBox(it, this) }) // TODO: 64-bit specific
private fun loadCallbacksLibrary() {
System.loadLibrary("callbacks")
}
/**
* Reference to `ffi_type` struct instance.
*/
internal class ffi_type (ptr: NativePtr) : NativeRef(ptr) {
companion object : Type<ffi_type>(::ffi_type)
}
/**
* Reference to `ffi_cif` struct instance.
*/
internal class ffi_cif (ptr: NativePtr) : NativeRef(ptr) {
companion object : Type<ffi_cif>(::ffi_cif)
}
private external fun ffiTypeVoid(): Long
private external fun ffiTypeUInt8(): Long
private external fun ffiTypeSInt8(): Long
private external fun ffiTypeUInt16(): Long
private external fun ffiTypeSInt16(): Long
private external fun ffiTypeUInt32(): Long
private external fun ffiTypeSInt32(): Long
private external fun ffiTypeUInt64(): Long
private external fun ffiTypeSInt64(): Long
private external fun ffiTypePointer(): Long
private external fun ffiTypeStruct0(elements: Long): Long
/**
* Allocates and initializes `ffi_type` describing the struct.
*
* @param elements types of the struct elements
*/
private fun ffiTypeStruct(elementTypes: List<ffi_type>): ffi_type {
val elements = mallocNativeArrayOf(ffi_type, *elementTypes.toTypedArray(), null).ptr
val res = ffiTypeStruct0(elements.value)
if (res == 0L) {
throw OutOfMemoryError()
}
return NativePtr.byValue(res).asRef(ffi_type)!!
}
private external fun ffiCreateCif0(nArgs: Int, rType: Long, argTypes: Long): Long
/**
* Creates and prepares an `ffi_cif`.
*
* @param returnType native function return value type
* @param paramTypes native function parameter types
*
* @return the initialized `ffi_cif`
*/
private fun ffiCreateCif(returnType: ffi_type, paramTypes: List<ffi_type>): ffi_cif {
val nArgs = paramTypes.size
val rType = returnType.ptr
val argTypes = mallocNativeArrayOf(ffi_type, *paramTypes.toTypedArray(), null).ptr
val res = ffiCreateCif0(nArgs, rType.value, argTypes.value)
when (res) {
0L -> throw OutOfMemoryError()
-1L -> throw Error("FFI_BAD_TYPEDEF")
-2L -> throw Error("FFI_BAD_ABI")
-3L -> throw Error("libffi error occurred")
}
return NativePtr.byValue(res).asRef(ffi_cif)!!
}
private fun ffiFunImpl0(ffiCif: Long, ret: Long, args: Long, userData: Any) {
ffiFunImpl(NativePtr.byValue(ffiCif).asRef(ffi_cif)!!,
NativePtr.byValue(ret)!!,
NativePtr.byValue(args).asRef(array(NativePtrBox))!!,
userData as (ret: NativePtr, args: NativeArray<NativePtrBox>) -> Unit)
}
/**
* This function is called from native code when a native function created with [ffiCreateClosure] is invoked.
*
* @param ret pointer to memory to be filled with return value of the invoked native function
* @param args pointer to array of pointers to arguments passed to the invoked native function
*/
private fun ffiFunImpl(ffiCif: ffi_cif, ret: NativePtr, args: NativeArray<NativePtrBox>,
userData: (NativePtr, NativeArray<NativePtrBox>) -> Unit) {
userData.invoke(ret, args)
}
private external fun ffiCreateClosure0(ffiCif: Long, userData: Any): Long
/**
* Uses libffi to allocate a native function which will call [ffiFunImpl] when invoked.
*
* @param ffiCif describes the type of the function to create
*/
private fun ffiCreateClosure(ffiCif: ffi_cif, userData: (NativePtr, NativeArray<NativePtrBox>) -> Unit): NativePtr {
val res = ffiCreateClosure0(ffiCif.ptr.value, userData)
when (res) {
0L -> throw OutOfMemoryError()
-1L -> throw Error("libffi error occurred")
}
return NativePtr.byValue(res)!!
}
private external fun newGlobalRef(any: Any): Long
private external fun derefGlobalRef(ref: Long): Any
private external fun deleteGlobalRef(ref: Long)
@@ -1,202 +0,0 @@
package kotlin_native.interop
import java.io.Closeable
// TODO: what about equals/hashCode?
open class NativeRef(val ptr: NativePtr) {
open class Type<T : NativeRef>(val byPtr: (NativePtr) -> T)
open class TypeWithSize<T : NativeRef>(val size: Int, byPtr: (NativePtr) -> T) : NativeRef.Type<T>(byPtr)
final override fun equals(other: Any?): Boolean{
if (this === other) return true
if (other !is NativeRef) return false
return ptr == other.ptr
}
final override fun hashCode(): Int{
return ptr.hashCode()
}
}
fun <T : NativeRef> NativePtr?.asRef(type: NativeRef.Type<T>) = this?.let { type.byPtr(this) }
fun NativeRef?.getNativePtr() = this?.ptr
class Int8Box(ptr: NativePtr) : NativeRef(ptr) {
companion object : NativeRef.TypeWithSize<Int8Box>(1, ::Int8Box)
var value: Byte
get() = bridge.getInt8(ptr)
set(value) = bridge.putInt8(ptr, value)
}
class Int16Box(ptr: NativePtr) : NativeRef(ptr) {
companion object : NativeRef.TypeWithSize<Int16Box>(2, ::Int16Box)
var value: Short
get() = bridge.getInt16(ptr)
set(value) = bridge.putInt16(ptr, value)
}
class Int32Box(ptr: NativePtr) : NativeRef(ptr) {
companion object : NativeRef.TypeWithSize<Int32Box>(4, ::Int32Box)
var value: Int
get() = bridge.getInt32(ptr)
set(value) = bridge.putInt32(ptr, value)
}
class NativePtrBox(ptr: NativePtr) : NativeRef(ptr) {
companion object : NativeRef.TypeWithSize<NativePtrBox>(8, ::NativePtrBox) // TODO: 64-bit specific
var value: NativePtr?
get() = bridge.getPtr(ptr)
set(value) = bridge.putPtr(ptr, value)
}
class Int64Box(ptr: NativePtr) : NativeRef(ptr) {
companion object : NativeRef.TypeWithSize<Int64Box>(8, ::Int64Box)
var value: Long
get() = bridge.getInt64(ptr)
set(value) = bridge.putInt64(ptr, value)
}
class RefBox<T : NativeRef>(ptr: NativePtr, val referentType: NativeRef.Type<T>) : NativeRef(ptr) {
companion object {
infix fun <T : NativeRef> of(type: NativeRef.Type<T>) = NativeRef.TypeWithSize<RefBox<T>>(8, { RefBox(it, type) }) // TODO: 64-bit specific
}
var value: T?
get() = bridge.getPtr(ptr).asRef(referentType)
set(value) = bridge.putPtr(ptr, value.getNativePtr())
}
val <T : NativeRef> NativeRef.Type<T>.ref: NativeRef.TypeWithSize<RefBox<T>>
get() = Ref to this
object Ref {
infix fun <T : NativeRef> to(type: NativeRef.Type<T>) = RefBox.Companion of type
infix fun to(type: Byte.Companion) = Int8Box.Companion
infix fun to(type: Short.Companion) = Int16Box.Companion
infix fun to(type: Int.Companion) = Int32Box.Companion
infix fun to(type: Long.Companion) = Int64Box.Companion
}
open class NativeStruct(ptr: NativePtr) : NativeRef(ptr) {
open class Type<T : NativeStruct>(size: Int, byPtr: (NativePtr) -> T) : NativeRef.TypeWithSize<T>(size, byPtr)
companion object {
class FieldAt<T : NativeRef>(val type: NativeRef.Type<T>, val offset: Int) {
operator fun getValue(thisRef: NativeStruct, property: kotlin.reflect.KProperty<*>): T {
return type.byPtr(thisRef.ptr.displacedBy(offset))
}
}
infix fun <T : NativeRef> NativeRef.Type<T>.at(offset: Int) = NativeStruct.Companion.FieldAt(this, offset)
}
}
class NativeArray<T : NativeRef>(ptr: NativePtr, val elemType: NativeRef.TypeWithSize<T>) : NativeRef(ptr) {
operator fun get(index: Int): T {
return elemType.byPtr(ptr.displacedBy(index * elemType.size))
}
companion object {
class Type<T : NativeRef> (val elemType: NativeRef.TypeWithSize<T>) :
NativeRef.Type<NativeArray<T>>({ ptr -> NativeArray(ptr, elemType) }) {
infix fun length(length: Int) =
NativeRef.TypeWithSize(elemType.size * length, { ptr -> NativeArray(ptr, elemType) })
}
infix fun <T : NativeRef> of(elemType: NativeRef.TypeWithSize<T>) = NativeArray.Companion.Type(elemType)
fun <T : NativeRef> byRefToFirstElem(ref: T, refType: NativeRef.TypeWithSize<T>) = NativeArray(ref.ptr, refType)
}
}
operator fun <T : NativeRef> NativeRef.TypeWithSize<T>.get(length: Int) = NativeArray.Companion of this length length
object array {
// array(type)
operator fun <T : NativeRef> invoke(type: NativeRef.TypeWithSize<T>) = NativeArray.Companion of type
// array[length](type)
operator fun get(length: Int) = array.ArrayWithLength(length)
class ArrayWithLength(val length: Int) {
infix fun <T : NativeRef> of(type: NativeRef.TypeWithSize<T>) = NativeArray.Companion of type length length
operator fun <T : NativeRef> invoke(type: NativeRef.TypeWithSize<T>) = this of type
}
}
class CString private constructor(internal val array: NativeArray<Int8Box>) : NativeRef(array.ptr) {
companion object {
fun fromArray(array: NativeArray<Int8Box>) = CString(array)
}
fun length(): Int {
var res = 0
while (array[res].value != 0.toByte()) {
++res
}
return res
}
override fun toString(): String {
val bytes = ByteArray(this.length())
bytes.forEachIndexed { i, byte ->
bytes[i] = this.array[i].value
}
return String(bytes) // TODO: encoding
}
fun asCharPtr() = array[0]
}
class __va_list_tag(ptr: NativePtr) : NativeRef(ptr) // FIXME
interface Placement {
fun alloc(size: Int): NativePtr
}
object heap : Placement {
override fun alloc(size: Int) = bridge.malloc(size)
fun free(ptr: NativePtr) = bridge.free(ptr)
fun free(ref: NativeRef) = free(ref.ptr)
}
// TODO: implement optimally
class Arena : Placement, Closeable {
private val allocatedChunks = mutableListOf<NativePtr>()
override fun alloc(size: Int): NativePtr {
val res = heap.alloc(size)
try {
allocatedChunks.add(res)
return res
} catch (e: Throwable) {
heap.free(res)
throw e
}
}
fun clear() {
allocatedChunks.forEach {
heap.free(it)
}
allocatedChunks.clear()
}
override fun close() = clear()
}
fun <T : NativeRef> Placement.alloc(type: NativeRef.TypeWithSize<T>): T {
return type.byPtr(this.alloc(type.size))
}
@@ -1,76 +0,0 @@
package kotlin_native.interop
fun <T : NativeRef> malloc(type: NativeRef.TypeWithSize<T>) = type.byPtr(bridge.malloc(type.size))
fun free(ptr: NativePtr?) {
if (ptr != null) {
bridge.free(ptr)
}
}
fun free(ref: NativeRef?) = free(ref.getNativePtr())
fun <T : NativeRef> Placement.allocNativeArrayOf(elemType: NativeRef.Type<T>, elements: List<T?>): NativeArray<RefBox<T>> {
val res = this.alloc(array[elements.size](elemType.ref))
elements.forEachIndexed { i, element ->
res[i].value = element
}
return res
}
fun <T : NativeRef> Placement.allocNativeArrayOf(elemType: NativeRef.Type<T>, vararg elements: T?) =
allocNativeArrayOf(elemType, elements.toList())
fun <T : NativeRef> mallocNativeArrayOf(elemType: NativeRef.Type<T>, vararg elements: T?) = heap.allocNativeArrayOf(elemType, *elements)
fun Placement.allocNativeArrayOf(elements: ByteArray): NativeArray<Int8Box> {
val res = this.alloc(array[elements.size](Int8Box))
elements.forEachIndexed { i, element ->
res[i].value = element
}
return res
}
fun CString.Companion.fromString(str: String?): CString? {
return str?.toCString(heap)
}
fun Int8Box.asCString() = CString.fromArray(NativeArray.byRefToFirstElem(this, Int8Box))
fun String.toCString(retValPlacement: Placement): CString {
val bytes = this.toByteArray() // TODO: encoding
val len = bytes.size
val nativeBytes = retValPlacement.alloc(array[len + 1](Int8Box))
bytes.forEachIndexed { i, byte ->
nativeBytes[i].value = byte
}
nativeBytes[len].value = 0
return CString.fromArray(nativeBytes)
}
class MemScope private constructor(private val arena: Arena) : Placement by arena {
val memScope: Placement
get() = this
companion object {
internal inline fun <R> use(block: MemScope.()->R): R {
val memScope = MemScope(Arena())
try {
return memScope.block()
} finally {
memScope.arena.clear()
}
}
}
}
/**
* Runs given [block] providing allocation of memory
* which will be automatically disposed at the end of this scope.
*/
inline fun <R> memScoped(block: MemScope.()->R): R {
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") // TODO: it is a hack
return MemScope.use(block)
}