From 399a90950a68a235d98712919d2f0f4b4dfda8ea Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Tue, 6 Sep 2016 12:02:22 +0300 Subject: [PATCH] experiments/MemLayout: replace prototype by another one --- .../.idea/runConfigurations/_1_MainKt.xml | 15 ++ experiments/MemLayout/native-src/bridge.c | 41 ++++ .../MemLayout/native-src/build-bridge.sh | 4 + experiments/MemLayout/src/_1/main.kt | 215 ++++++++++++++++++ experiments/MemLayout/src/app.kt | 97 -------- experiments/MemLayout/src/bridge.kt | 64 ++++++ 6 files changed, 339 insertions(+), 97 deletions(-) create mode 100644 experiments/MemLayout/.idea/runConfigurations/_1_MainKt.xml create mode 100644 experiments/MemLayout/native-src/bridge.c create mode 100755 experiments/MemLayout/native-src/build-bridge.sh create mode 100644 experiments/MemLayout/src/_1/main.kt delete mode 100644 experiments/MemLayout/src/app.kt create mode 100644 experiments/MemLayout/src/bridge.kt diff --git a/experiments/MemLayout/.idea/runConfigurations/_1_MainKt.xml b/experiments/MemLayout/.idea/runConfigurations/_1_MainKt.xml new file mode 100644 index 00000000000..26955536745 --- /dev/null +++ b/experiments/MemLayout/.idea/runConfigurations/_1_MainKt.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/experiments/MemLayout/native-src/bridge.c b/experiments/MemLayout/native-src/bridge.c new file mode 100644 index 00000000000..efa8c998ef9 --- /dev/null +++ b/experiments/MemLayout/native-src/bridge.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include + +JNIEXPORT jlong JNICALL Java_bridge_findSym(JNIEnv *env, jobject bridge, jstring name) { + char* nameChars = (*env)->GetStringUTFChars(env, name, NULL); + jlong result = dlsym(RTLD_DEFAULT, nameChars); + (*env)->ReleaseStringUTFChars(env, name, nameChars); + return result; +} + +JNIEXPORT jlong JNICALL Java_bridge_call0(JNIEnv *env, jobject obj, jlong ptr) { + intptr_t (*func)() = ptr; + return func(); + +} + +JNIEXPORT jlong JNICALL Java_bridge_call1(JNIEnv *env, jobject obj, jlong ptr, jlong arg1) { + intptr_t (*func)(intptr_t) = ptr; + return func(arg1); + +} + +JNIEXPORT jlong JNICALL Java_bridge_call2(JNIEnv *env, jobject obj, jlong ptr, jlong arg1, jlong arg2) { + intptr_t (*func)(intptr_t, intptr_t) = ptr; + return func(arg1, arg2); + +} + +JNIEXPORT jlong JNICALL Java_bridge_call3(JNIEnv *env, jobject obj, jlong ptr, jlong arg1, jlong arg2, jlong arg3) { + intptr_t (*func)(intptr_t, intptr_t, intptr_t) = ptr; + return func(arg1, arg2, arg3); + +} + +JNIEXPORT jlong JNICALL Java_bridge_call4(JNIEnv *env, jobject obj, jlong ptr, jlong arg1, jlong arg2, jlong arg3, jlong arg4) { + intptr_t (*func)(intptr_t, intptr_t, intptr_t, intptr_t) = ptr; + return func(arg1, arg2, arg3, arg4); + +} diff --git a/experiments/MemLayout/native-src/build-bridge.sh b/experiments/MemLayout/native-src/build-bridge.sh new file mode 100755 index 00000000000..887fc152bc6 --- /dev/null +++ b/experiments/MemLayout/native-src/build-bridge.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +clang -shared -o../out/production/MemLayout/libbridge.dylib bridge.c -I /System/Library/Frameworks/JavaVM.framework/Headers + diff --git a/experiments/MemLayout/src/_1/main.kt b/experiments/MemLayout/src/_1/main.kt new file mode 100644 index 00000000000..d013e5011c6 --- /dev/null +++ b/experiments/MemLayout/src/_1/main.kt @@ -0,0 +1,215 @@ +package _1 +import NativeFunction +import kotlin.reflect.KProperty + +operator fun NativeFunction.invoke(vararg args: Any?): Long { + val convertedArgs = LongArray(args.size) + + val allocatedArgs = mutableListOf() + args.forEachIndexed { i, arg -> + val convertedArg = when (arg) { + null -> 0L + is Long -> arg + is NativeStructPtr -> arg.ptr + is String -> { + val cString = CStringPtr.fromString(arg) + allocatedArgs.add(cString) + cString.ptr + } + is Byte -> arg.toLong() + is Char -> arg.toLong() + is Short -> arg.toLong() + is Int -> arg.toLong() + else -> throw NotImplementedError() + } + convertedArgs[i] = convertedArg + } + try { + return bridge.call(this.ptr, *convertedArgs) + } finally { + allocatedArgs.forEach { bridge.free(it.ptr) } + } +} + +open class NativeStructPtr(val ptr: Long) { + companion object { + class ByteField(val offset: Int) { + operator fun getValue(thisRef: NativeStructPtr, property: KProperty<*>): Byte { + return bridge.getByte(thisRef.ptr + offset) + } + + operator fun setValue(thisRef: NativeStructPtr, property: KProperty<*>, value: Byte) { + bridge.putByte(thisRef.ptr + offset, value) + } + } + + class ShortField(val offset: Int) { + operator fun getValue(thisRef: NativeStructPtr, property: KProperty<*>): Short { + return bridge.getShort(thisRef.ptr + offset) + } + + operator fun setValue(thisRef: NativeStructPtr, property: KProperty<*>, value: Short) { + bridge.putShort(thisRef.ptr + offset, value) + } + } + + class IntField(val offset: Int) { + operator fun getValue(thisRef: NativeStructPtr, property: KProperty<*>): Int { + return bridge.getInt(thisRef.ptr + offset) + } + + operator fun setValue(thisRef: NativeStructPtr, property: KProperty<*>, value: Int) { + bridge.putInt(thisRef.ptr + offset, value) + } + } + + class LongField(val offset: Int) { + operator fun getValue(thisRef: NativeStructPtr, property: KProperty<*>): Long { + return bridge.getLong(thisRef.ptr + offset) + } + + operator fun setValue(thisRef: NativeStructPtr, property: KProperty<*>, value: Long) { + bridge.putLong(thisRef.ptr + offset, value) + } + } + + class StructField(val cast: (Long) -> T, val offset: Int) { + operator fun getValue(thisRef: NativeStructPtr, property: KProperty<*>): T { + return cast(thisRef.ptr + offset); + } + } + + fun byteAt(offset: Int) = ByteField(offset) + fun shortAt(offset: Int) = ShortField(offset) + fun intAt(offset: Int) = IntField(offset) + fun longAt(offset: Int) = LongField(offset) + fun structAt(cast: (Long) -> T, offset: Int) = StructField(cast, offset) + } +} + +class Layout(val size: Int, val cast: (Long) -> T); + +class PtrBox(ptr: Long) : NativeStructPtr(ptr) { + companion object { + val layout = Layout(8, ::PtrBox) + } +} + + +class timespec(ptr: Long) : NativeStructPtr(ptr) { + val tv_sec by longAt(0) + val tv_usec by longAt(8) +} + +class stat64(ptr: Long) : NativeStructPtr(ptr) { + val st_dev by intAt(0) + val st_mode by shortAt(4) + val st_nlink by shortAt(6) + val st_ino by longAt(8) + val st_atimespec by structAt(::timespec, 32) + val st_mtimespec by structAt(::timespec, 48) + val st_ctimespec by structAt(::timespec, 64) + val st_birthtimespec by structAt(::timespec, 80) + val st_size by longAt(96) + + companion object { + val layout = Layout(144, ::stat64) + } + +} +val stat64Fun = NativeFunction.lookup("stat\$INODE64")!! + +class CStringPtr(ptr: Long) : NativeStructPtr(ptr) { + + companion object { + fun fromString(str: String): CStringPtr { + val bytes = str.toByteArray(); // FIXME: encoding + val ptr = bridge.malloc(bytes.size + 1) + bytes.forEachIndexed { i, byte -> + bridge.putByte(ptr + i, byte) + } + bridge.putByte(ptr + bytes.size, 0) + return CStringPtr(ptr) + } + } + + override fun toString(): String { + var length = 0 + while (bridge.getByte(ptr + length) != 0.toByte()) { + ++length + } + + val bytes = ByteArray(length) + for (i in 0 .. length-1) { + bytes.set(i, bridge.getByte(ptr + i)) + } + + return String(bytes) // FIXME: encoding + } + +} + +class dirent64(ptr: Long) : NativeStructPtr(ptr) { + val d_ino by longAt(0) + val d_seekoff by longAt(8) + val d_reclen by shortAt(16) + val d_namelen by shortAt(18) + val d_type by byteAt(20) + val d_name by structAt(::CStringPtr, 21) + + + companion object { + val layout = Layout(1048, ::dirent64) + } +} + +val opendir = NativeFunction.lookup("opendir")!! +val readdir_r64 = NativeFunction.lookup("readdir_r\$INODE64")!! +val closedir = NativeFunction.lookup("closedir")!! + +fun malloc(layout: Layout) = layout.cast(bridge.malloc(layout.size)) +fun malloc(layout: Layout, action: (T) -> R): R { + val s = malloc(layout) + try { + return action(s) + } finally { + bridge.free(s.ptr) + } +} + +fun main(args: Array) { + + malloc(stat64.layout) { s -> + val res = stat64Fun("/etc/passwd", s) + println(res) + with(s) { + println("st_dev=$st_dev") + println("st_mode=$st_mode") + println("st_nlink=$st_nlink") + println("st_ino=$st_ino") + println("st_mtimespec.tv_sec=${st_mtimespec.tv_sec}") + println("st_size=$st_size") + } + } + + val dirp = opendir("/tmp") + try { + malloc(dirent64.layout) { entry -> + malloc(PtrBox.layout) { result -> + + while (true) { + val errno = readdir_r64(dirp, entry, result) + if (bridge.getLong(result.ptr) == 0L) { + break + } + println(entry.d_name) + } + + } + } + } finally { + closedir(dirp) + } + + +} \ No newline at end of file diff --git a/experiments/MemLayout/src/app.kt b/experiments/MemLayout/src/app.kt deleted file mode 100644 index 127223c5622..00000000000 --- a/experiments/MemLayout/src/app.kt +++ /dev/null @@ -1,97 +0,0 @@ -package kotlin_native.test - -import sun.misc.Unsafe - -class Bridge { - val unsafe = GetUnsafe() - - fun GetUnsafe(): sun.misc.Unsafe { - val theUnsafe = Unsafe::class.java.getDeclaredField("theUnsafe") - theUnsafe.isAccessible = true - return theUnsafe.get(null) as Unsafe - } - - constructor() { - System.loadLibrary("bridge-jni") - } - - fun Lookup(name:String) : NativePtr; - - public native fun Call(NativeFunction func, Array args) - public native fun Lookup(String func, String signature) : NativeFunction -} - -val bridge = Bridge() - -class NativeFunction(name:String, val signature:String) { - var ptr : Long = bridge.LookupFunction(name) -} - -val stat = NativeFunction("stat". "DSP") - -class NativePtr(size:Long) { - var ptr : Long = bridge.unsafe.allocateMemory(size) - - fun IntAt(offset:Int):Int { - return bridge.unsafe.getInt(ptr+offset) - } - fun LongAt(offset:Int):Long { - return bridge.unsafe.getLong(ptr+offset) - } - fun Free() { - bridge.unsafe.freeMemory(ptr) - ptr = 0 - } -} - -interface Native { - fun ptr() : NativePtr -} - -class StatInfo : Native { - val ptr = NativePtr(1024) - val size : Long - get() = ptr.LongAt(96) - val inode : Int - get() = ptr.IntAt(8) - override fun ptr() = ptr -} - -fun AsCString(s:String):NativePtr { - val ptr = NativePtr((s.length + 1) as Long) - return ptr -} - -class Layout { - fun Call(func: String, signature: String, args:Array) : Any { - - when (func) { - "stat" -> { - val str = AsCString(args[0] as String) - val args2 = arrayOf(null, str, (args[1] as StatInfo).ptr) - val rv = bridge.Call(func, signature, args2) - str.Free() - return args2[0] - } - else -> assert(false) - } - return "" - } -} - -fun Cleanup(args:Array) { - for (a in args) { - if (a is Native) { - a.ptr().Free() - } - } -} - -fun main(args: Array) { - val caller = Layout() - val file = if (args.size > 0) args[0] else "/etc/passwd" - val args = arrayOf(file, StatInfo()) - val result = caller.Call(bridge.Lookup("stat", "DSP"), args) - println("got " + (args[1] as StatInfo).size) - Cleanup(args) -} \ No newline at end of file diff --git a/experiments/MemLayout/src/bridge.kt b/experiments/MemLayout/src/bridge.kt new file mode 100644 index 00000000000..5261bf22059 --- /dev/null +++ b/experiments/MemLayout/src/bridge.kt @@ -0,0 +1,64 @@ +import sun.misc.Unsafe + +object bridge { + + init { + System.loadLibrary("bridge") + } + + fun lookup(name: String): Long { + return findSym(name) + } + + fun call(funcPtr: Long, vararg args: Long): Long { + when (args.size) { + 0 -> return call0(funcPtr) + 1 -> return call1(funcPtr, args[0]) + 2 -> return call2(funcPtr, args[0], args[1]) + 3 -> return call3(funcPtr, args[0], args[1], args[2]) + 4 -> return call4(funcPtr, args[0], args[1], args[2], args[3]) + else -> throw NotImplementedError() + } + } + + private external fun findSym(name: String): Long + private external fun call0(ptr: Long): Long + private external fun call1(ptr: Long, arg1: Long): Long + private external fun call2(ptr: Long, arg1: Long, arg2: Long): Long + private external fun call3(ptr: Long, arg1: Long, arg2: Long, arg3: Long): Long + private external fun call4(ptr: Long, arg1: Long, arg2: Long, arg3: Long, arg4: Long): Long + + fun malloc(size: Int): Long = unsafe.allocateMemory(size.toLong()) + fun free(ptr: Long) = unsafe.freeMemory(ptr) + + fun getLong(ptr: Long): Long = unsafe.getLong(ptr) + fun putLong(ptr: Long, value: Long) = unsafe.putLong(ptr, value) + + fun getInt(ptr: Long): Int = unsafe.getInt(ptr) + fun putInt(ptr: Long, value: Int) = unsafe.putInt(ptr, value) + + fun getShort(ptr: Long): Short = unsafe.getShort(ptr) + fun putShort(ptr: Long, value: Short) = unsafe.putShort(ptr, value) + + fun getByte(ptr: Long): Byte = unsafe.getByte(ptr) + fun putByte(ptr: Long, value: Byte) = unsafe.putByte(ptr, value) + + private val unsafe = with(Unsafe::class.java.getDeclaredField("theUnsafe")) { + isAccessible = true + return@with this.get(null) as Unsafe + } +} + +class NativeFunction(val ptr: Long) { + + companion object { + fun lookup(name: String): NativeFunction? { + val funcPtr = bridge.lookup(name) + return if (funcPtr == 0L) { + null + } else { + NativeFunction(funcPtr) + } + } + } +} \ No newline at end of file