Coverage Summary for Class: FFM (dev.suresh.lang)
Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
FFM |
0%
(0/12)
|
0%
(0/22)
|
0%
(0/121)
|
0%
(0/809)
|
FFM$memoryLayout$$inlined$runOnVirtualThread$1 |
0%
(0/1)
|
|
Total |
0%
(0/13)
|
0%
(0/22)
|
0%
(0/121)
|
0%
(0/809)
|
package dev.suresh.lang
import dev.suresh.*
import io.github.oshai.kotlinlogging.KLogger
import java.lang.foreign.*
import java.lang.invoke.MethodHandle
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType
import java.time.Instant
object FFM {
suspend fun memoryLayout(logger: KLogger) = runOnVirtualThread {
with(logger) {
memoryAPIs()
currTime()
strlen("Hello Panama!")
getPid()
gmtime()
terminal()
dhReflection()
}
}
private fun KLogger.strlen(str: String) {
val strlenAddr = SYMBOL_LOOKUP.findOrNull("strlen")
val strlenDescriptor = FunctionDescriptor.of(ValueLayout.JAVA_INT, AddressLayout.ADDRESS)
val strlen = LINKER.downcallHandle(strlenAddr, strlenDescriptor)
Arena.ofConfined().use { arena ->
val cString = arena.allocateFrom(str)
val strlenResult = strlen.invokeExact(cString) as Int
info { """strlen("$str") = $strlenResult""" }
}
}
private fun KLogger.currTime() {
// Print the current time.
val timeAddr = SYMBOL_LOOKUP.findOrNull("time")
val timeDesc = FunctionDescriptor.of(ValueLayout.JAVA_LONG)
val time = LINKER.downcallHandle(timeAddr, timeDesc)
val timeResult = time.invokeExact() as Long
info { "time() = $timeResult epochSecond" }
}
private fun KLogger.gmtime() {
val gmtAddr = SYMBOL_LOOKUP.findOrNull("gmtime")
val gmtDesc =
FunctionDescriptor.of(
AddressLayout.ADDRESS.withTargetLayout(TM.LAYOUT), AddressLayout.ADDRESS)
val gmtime = LINKER.downcallHandle(gmtAddr, gmtDesc)
Arena.ofConfined().use { arena ->
val time = arena.allocate(ValueLayout.JAVA_LONG.byteSize())
time.set(ValueLayout.JAVA_LONG, 0, Instant.now().epochSecond)
val tmSegment = gmtime.invokeExact(time) as MemorySegment
info { "gmtime() = ${TM(tmSegment)}" }
}
}
private fun KLogger.getPid() {
val getpidAddr = SYMBOL_LOOKUP.findOrNull("getpid")
val getpidDesc = FunctionDescriptor.of(ValueLayout.JAVA_INT)
val getpid = LINKER.downcallHandle(getpidAddr, getpidDesc)
val pid = getpid.invokeExact() as Int
assert(pid.toLong() == ProcessHandle.current().pid())
info { "getpid() = $pid" }
}
/**
* Allocate memory for two doubles and initialize it.
*
* ```c
* Struct Point2D {
* double x;
* double y;
* } point = { 1.0, 2.0 };
* ```
*/
private fun KLogger.memoryAPIs() {
Arena.ofConfined().use { arena ->
val point = arena.allocate(ValueLayout.JAVA_DOUBLE.byteSize() * 2)
point.set(ValueLayout.JAVA_DOUBLE, 0, 1.0)
point.set(ValueLayout.JAVA_DOUBLE, 8, 2.0)
info { "Point Struct = $point" }
info {
"""Struct {
| x = ${point.get(ValueLayout.JAVA_DOUBLE, 0)} ,
| y = ${point.get(ValueLayout.JAVA_DOUBLE, 8)}
|}
"""
.trimMargin()
}
}
val point2D =
MemoryLayout.structLayout(
ValueLayout.JAVA_DOUBLE.withName("x"),
ValueLayout.JAVA_DOUBLE.withName("y"),
)
.withName("Point2D")
// VarHandle accessors for the struct fields
val x = point2D.varHandle(MemoryLayout.PathElement.groupElement("x"))
val y = point2D.varHandle(MemoryLayout.PathElement.groupElement("y"))
Arena.ofConfined().use { arena ->
// val seg = MemorySegment.allocateNative(8,arena.scope())
val point = arena.allocate(point2D)
x.set(point, 0L, 1.0)
y.set(point, 0L, 2.0)
info { "Point2D segment = $point" }
info {
"""Point2D {
| x = ${x.get(point, 0L)} ,
| y = ${y.get(point, 0L)}
|}"""
.trimMargin()
}
}
// Allocate an off-heap region of memory big enough to hold 10 values of the primitive type int,
// and fill it with values ranging from 0 to 9
Arena.ofConfined().use { arena ->
val count = 10
val segment = arena.allocate(count * ValueLayout.JAVA_INT.byteSize())
for (i in 0..<count) {
segment.setAtIndex(ValueLayout.JAVA_INT, i.toLong(), i)
}
}
}
private fun KLogger.terminal() {
val winsize =
MemoryLayout.structLayout(
ValueLayout.JAVA_SHORT.withName("ws_row"),
ValueLayout.JAVA_SHORT.withName("ws_col"),
ValueLayout.JAVA_SHORT.withName("ws_xpixel"),
ValueLayout.JAVA_SHORT.withName("ws_ypixel"),
)
.withName("winsize")
val wsRow = winsize.varHandle(MemoryLayout.PathElement.groupElement("ws_row"))
val wsCol = winsize.varHandle(MemoryLayout.PathElement.groupElement("ws_col"))
val wsXpixel = winsize.varHandle(MemoryLayout.PathElement.groupElement("ws_xpixel"))
val wsYpixel = winsize.varHandle(MemoryLayout.PathElement.groupElement("ws_ypixel"))
val ioctlFun =
FunctionDescriptor.of(
ValueLayout.JAVA_INT,
ValueLayout.JAVA_INT,
ValueLayout.JAVA_LONG,
AddressLayout.ADDRESS.withTargetLayout(winsize))
val isAttyFun = FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT)
// For capturing the errno value
val ccs = Linker.Option.captureCallState("errno")
val csLayout = Linker.Option.captureStateLayout()
val errnoHandle = csLayout?.varHandle(MemoryLayout.PathElement.groupElement("errno"))
val ioctl = downcallHandle("ioctl", ioctlFun, ccs, Linker.Option.firstVariadicArg(2))
val isAtty = downcallHandle("isatty", isAttyFun)
Arena.ofConfined().use { arena ->
val isTerminal = isAtty?.invokeExact(1) as Int != 0
if (isTerminal) {
val winSeg = arena.allocate(winsize)
val capturedState = arena.allocate(csLayout)
val winRet = ioctl?.invokeExact(capturedState, 1, 0x40087468L, winSeg) as Int
if (winRet == -1) {
val errno = errnoHandle?.get(capturedState, 0L) as Int
info { "ioctl() errno: $errno" }
} else {
info {
"""
|winsize {
| ws_row = ${wsRow.get(winSeg, 0L)}
| ws_col = ${wsCol.get(winSeg, 0L)}
| ws_xpixel = ${wsXpixel.get(winSeg, 0L)}
| ws_ypixel = ${wsYpixel.get(winSeg, 0L)}
|}
"""
.trimMargin()
}
}
} else {
info { "Not a TTY" }
}
}
/** Atomic access for long[] using a MemorySegment */
fun arrayAtomicAccess() {
val arr = LongArray(10)
val arrSeg = MemorySegment.ofArray(arr)
val vh = ValueLayout.JAVA_INT.varHandle()
vh.setVolatile(arrSeg, 0L, 69)
assert(vh.getVolatile(arrSeg, 0L) as Int == 69)
}
}
/** Reflectively invoke the downcallHandle method on the Linker class. */
private fun KLogger.dhReflection() {
val mh =
MethodHandles.lookup()
.findVirtual(
Linker::class.java,
"downcallHandle",
MethodType.methodType(
MethodHandle::class.java,
FunctionDescriptor::class.java,
Array<Linker.Option>::class.java,
),
)
val handle =
mh.invokeExact(LINKER, FunctionDescriptor.ofVoid(), arrayOf<Linker.Option>())
as MethodHandle
info { "Got downcall handle: $handle" }
}
}