experiments/MemLayout: replace prototype by another one

This commit is contained in:
Svyatoslav Scherbina
2016-09-06 12:02:22 +03:00
parent 0e8698ee3e
commit 399a90950a
6 changed files with 339 additions and 97 deletions
@@ -0,0 +1,15 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="_1.MainKt" type="JetRunConfigurationType" factoryName="Kotlin" nameIsGenerated="true">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="_1.MainKt" />
<option name="VM_PARAMETERS" value="-Djava.library.path=$PROJECT_DIR$/out/production/MemLayout" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PASS_PARENT_ENVS" value="true" />
<module name="MemLayout" />
<envs />
<method />
</configuration>
</component>
+41
View File
@@ -0,0 +1,41 @@
#include <jni.h>
#include <dlfcn.h>
#include <stdint.h>
#include <stdio.h>
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);
}
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
clang -shared -o../out/production/MemLayout/libbridge.dylib bridge.c -I /System/Library/Frameworks/JavaVM.framework/Headers
+215
View File
@@ -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<NativeStructPtr>()
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<T : NativeStructPtr>(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 <T : NativeStructPtr> structAt(cast: (Long) -> T, offset: Int) = StructField(cast, offset)
}
}
class Layout<T : NativeStructPtr>(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 <T : NativeStructPtr> malloc(layout: Layout<T>) = layout.cast(bridge.malloc(layout.size))
fun <T : NativeStructPtr, R> malloc(layout: Layout<T>, action: (T) -> R): R {
val s = malloc(layout)
try {
return action(s)
} finally {
bridge.free(s.ptr)
}
}
fun main(args: Array<String>) {
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)
}
}
-97
View File
@@ -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<Any> 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>) : 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<Any>) {
for (a in args) {
if (a is Native) {
a.ptr().Free()
}
}
}
fun main(args: Array<String>) {
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)
}
+64
View File
@@ -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)
}
}
}
}