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