experiments/MemLayout: replace prototype by another one
This commit is contained in:
@@ -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>
|
||||
@@ -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
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
clang -shared -o../out/production/MemLayout/libbridge.dylib bridge.c -I /System/Library/Frameworks/JavaVM.framework/Headers
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user