WARNING! Access to this system is limited to authorised users only.
Unauthorised users may be subject to prosecution.
Unauthorised access to this system is a criminal offence under Australian law (Federal Crimes Act 1914 Part VIA)
It is a criminal offence to:
(1) Obtain access to data without authority. -Penalty 2 years imprisonment.
(2) Damage, delete, alter or insert data without authority. -Penalty 10 years imprisonment.
User activity is monitored and recorded. Anyone using this system expressly consents to such monitoring and recording.

To protect your data, the CISO officer has suggested users to enable 2FA as soon as possible.
Currently 2.6% of users enabled 2FA.

Commit 64c681d8 authored by Kunshan Wang's avatar Kunshan Wang
Browse files

More MuCtx methods and tests.

A C client can now call API functions to convert between primitive C
values and Mu handles.
parent 607995f8
...@@ -17,6 +17,7 @@ lazy val root = (project in file(".")).settings( ...@@ -17,6 +17,7 @@ lazy val root = (project in file(".")).settings(
scalaVersion := "2.11.7", scalaVersion := "2.11.7",
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"org.scala-lang" % "scala-reflect" % "2.11.7",
"org.antlr" % "antlr4" % "4.5.1-1", "org.antlr" % "antlr4" % "4.5.1-1",
"com.typesafe.scala-logging" %% "scala-logging" % "3.1.0", "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0",
"ch.qos.logback" % "logback-classic" % "1.1.3", "ch.qos.logback" % "logback-classic" % "1.1.3",
......
...@@ -134,13 +134,13 @@ struct MuCtx { ...@@ -134,13 +134,13 @@ struct MuCtx {
void (*load_hail )(MuCtx *ctx, char *buf, int sz); void (*load_hail )(MuCtx *ctx, char *buf, int sz);
// Convert from C values to Mu values // Convert from C values to Mu values
MuIntValue (*handle_from_int8 )(MuCtx *ctx, int8_t num, int len); MuIntValue (*handle_from_sint8 )(MuCtx *ctx, int8_t num, int len);
MuIntValue (*handle_from_uint8 )(MuCtx *ctx, uint8_t num, int len); MuIntValue (*handle_from_uint8 )(MuCtx *ctx, uint8_t num, int len);
MuIntValue (*handle_from_int16 )(MuCtx *ctx, int16_t num, int len); MuIntValue (*handle_from_sint16)(MuCtx *ctx, int16_t num, int len);
MuIntValue (*handle_from_uint16)(MuCtx *ctx, uint16_t num, int len); MuIntValue (*handle_from_uint16)(MuCtx *ctx, uint16_t num, int len);
MuIntValue (*handle_from_int32 )(MuCtx *ctx, int32_t num, int len); MuIntValue (*handle_from_sint32)(MuCtx *ctx, int32_t num, int len);
MuIntValue (*handle_from_uint32)(MuCtx *ctx, uint32_t num, int len); MuIntValue (*handle_from_uint32)(MuCtx *ctx, uint32_t num, int len);
MuIntValue (*handle_from_int64 )(MuCtx *ctx, int64_t num, int len); MuIntValue (*handle_from_sint64)(MuCtx *ctx, int64_t num, int len);
MuIntValue (*handle_from_uint64)(MuCtx *ctx, uint64_t num, int len); MuIntValue (*handle_from_uint64)(MuCtx *ctx, uint64_t num, int len);
MuFloatValue (*handle_from_float )(MuCtx *ctx, float num); MuFloatValue (*handle_from_float )(MuCtx *ctx, float num);
MuDoubleValue (*handle_from_double)(MuCtx *ctx, double num); MuDoubleValue (*handle_from_double)(MuCtx *ctx, double num);
......
package uvm.refimpl.nat package uvm.refimpl.nat
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import scala.collection.mutable.HashMap import scala.collection.mutable.HashMap
import scala.collection.mutable.HashSet
import scala.reflect.ClassTag import scala.reflect.ClassTag
import scala.reflect.runtime.universe import scala.reflect.runtime.universe
import scala.reflect.runtime.{ universe => ru } import scala.reflect.runtime.{ universe => ru }
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import com.kenai.jffi.CallingConvention import com.kenai.jffi.CallingConvention
import com.kenai.jffi.Closure import com.kenai.jffi.Closure
import com.kenai.jffi.Closure.Buffer import com.kenai.jffi.Closure.Buffer
import com.kenai.jffi.{ Type => JType } import com.kenai.jffi.{ Type => JType }
import com.typesafe.scalalogging.Logger import com.typesafe.scalalogging.Logger
import NativeSupport._ import NativeSupport._
import PlatformConstants.WORD_SIZE_BYTES import PlatformConstants.WORD_SIZE_BYTES
import PlatformConstants.Word import PlatformConstants.Word
import jnr.ffi.ObjectReferenceManager import jnr.ffi.ObjectReferenceManager
import jnr.ffi.Pointer import jnr.ffi.Pointer
import uvm.refimpl._ import uvm.refimpl._
import scala.collection.mutable.HashSet
/** /**
* This object calls a C function to handle the trap. * This object calls a C function to handle the trap.
...@@ -32,17 +36,16 @@ class NativeTrapHandler(val funcAddr: Long, val userdata: Long) extends TrapHand ...@@ -32,17 +36,16 @@ class NativeTrapHandler(val funcAddr: Long, val userdata: Long) extends TrapHand
* This object routes calls from native clients to the MicroVM object. * This object routes calls from native clients to the MicroVM object.
*/ */
object NativeMuVM { object NativeMuVM {
// Syntax sugar for MuCtx and MuValue return values, which are pointers import NativeMuHelpers._
type MuCtxPtr = Word import NativeClientSupport._
type MuValuePtr = Word
def new_context(microVM: MicroVM): MuCtxPtr = { def new_context(microVM: MicroVM): MuCtxPtr = {
val ctx = microVM.newContext() val ctx = microVM.newContext()
val ptr = NativeClientSupport.exposeMuCtx(ctx) val ptr = exposeMuCtx(ctx)
ptr ptr
} }
def id_of(microVM: MicroVM, name: String): Int = microVM.idOf(name) def id_of(microVM: MicroVM, name: MuName): MuID = microVM.idOf(name)
def name_of(microVM: MicroVM, id: Int): String = microVM.nameOf(id) def name_of(microVM: MicroVM, id: MuID): MuName = microVM.nameOf(id)
def set_trap_handler(microVM: MicroVM, trap_handler: Word, userdata: Word): Unit = { def set_trap_handler(microVM: MicroVM, trap_handler: Word, userdata: Word): Unit = {
microVM.setTrapHandler(new NativeTrapHandler(trap_handler, userdata)) microVM.setTrapHandler(new NativeTrapHandler(trap_handler, userdata))
} }
...@@ -50,27 +53,99 @@ object NativeMuVM { ...@@ -50,27 +53,99 @@ object NativeMuVM {
/** /**
* This object routes calls from native clients to a MuCtx object. * This object routes calls from native clients to a MuCtx object.
* <p>
* Parameters are usually auto-converted from low-level types (pointers to function tables or C MuValue pointer), but
* return values are usually explicitly converted to pointers by the "expose*" methods, such as exposeMuValue.
*/ */
object NativeMuCtx { object NativeMuCtx {
// Syntax sugar for MuCtx and MuValue return values, which are pointers import NativeMuHelpers._
type MuCtxPtr = Word import NativeClientSupport._
type MuValuePtr = Word
def id_of(ctx: MuCtx, name: String): Int = ctx.idOf(name) def id_of(ctx: MuCtx, name: MuName): MuID = ctx.idOf(name)
def name_of(ctx: MuCtx, id: Int): String = ctx.nameOf(id) def name_of(ctx: MuCtx, id: MuID): MuName = ctx.nameOf(id)
/** /**
* NOTE: Should have taken MuCtx as the first parameter, but we need to de-allocate * NOTE: Should have taken MuCtx as the first parameter, but we need to de-allocate
* the function table, too. * the function table, too.
*/ */
def close_context(funcTableAddr: Word): Unit = { def close_context(funcTableAddr: Word): Unit = {
val ctx = NativeClientSupport.getObjNotNull(funcTableAddr, NativeClientSupport.muCtxs) val ctx = getObjFromFuncTableAddrNotNull(funcTableAddr, NativeClientSupport.muCtxs)
for (muValueAddr <- NativeClientSupport.muCtxToMuValues.remove(ctx).get) { unexposeMuCtx(funcTableAddr)
NativeClientSupport.unexposeMuValue(muValueAddr)
}
NativeClientSupport.unexposeMuCtx(funcTableAddr)
ctx.closeContext() ctx.closeContext()
} }
def load_bundle(ctx: MuCtx, buf: Word, sz: Int): Unit = {
val str = theMemory.getString(buf, sz, StandardCharsets.US_ASCII)
ctx.loadBundle(str)
}
def load_hail(ctx: MuCtx, buf: Word, sz: Int): Unit = {
val str = theMemory.getString(buf, sz, StandardCharsets.US_ASCII)
ctx.loadHail(str)
}
def handle_from_sint8(ctx: MuCtx, num: Byte, len: Int): MuValuePtr = handleFromInt(ctx, num, len)
def handle_from_uint8(ctx: MuCtx, num: Byte, len: Int): MuValuePtr = handleFromInt(ctx, trunc(num, 8), len)
def handle_from_sint16(ctx: MuCtx, num: Short, len: Int): MuValuePtr = handleFromInt(ctx, num, len)
def handle_from_uint16(ctx: MuCtx, num: Short, len: Int): MuValuePtr = handleFromInt(ctx, trunc(num, 16), len)
def handle_from_sint32(ctx: MuCtx, num: Int, len: Int): MuValuePtr = handleFromInt(ctx, num, len)
def handle_from_uint32(ctx: MuCtx, num: Int, len: Int): MuValuePtr = handleFromInt(ctx, trunc(num, 32), len)
def handle_from_sint64(ctx: MuCtx, num: Long, len: Int): MuValuePtr = handleFromInt(ctx, num, len)
def handle_from_uint64(ctx: MuCtx, num: Long, len: Int): MuValuePtr = handleFromInt(ctx, trunc(num, 64), len)
def handle_from_float(ctx: MuCtx, num: Float): MuValuePtr = exposeMuValue(ctx, ctx.handleFromFloat(num))
def handle_from_double(ctx: MuCtx, num: Double): MuValuePtr = exposeMuValue(ctx, ctx.handleFromDouble(num))
def handle_from_ptr(ctx: MuCtx, mu_type: MuID, ptr: MuCPtr): MuValuePtr = exposeMuValue(ctx, ctx.handleFromPtr(mu_type, ptr))
def handle_from_fp(ctx: MuCtx, mu_type: MuID, fp: MuCFP): MuValuePtr = exposeMuValue(ctx, ctx.handleFromFP(mu_type, fp))
def handle_to_sint8(ctx: MuCtx, opnd: MuIntValue): Byte = ctx.handleToSInt(opnd).toByte
def handle_to_uint8(ctx: MuCtx, opnd: MuIntValue): Byte = ctx.handleToUInt(opnd).toByte
def handle_to_sint16(ctx: MuCtx, opnd: MuIntValue): Short = ctx.handleToSInt(opnd).toShort
def handle_to_uint16(ctx: MuCtx, opnd: MuIntValue): Short = ctx.handleToUInt(opnd).toShort
def handle_to_sint32(ctx: MuCtx, opnd: MuIntValue): Int = ctx.handleToSInt(opnd).toInt
def handle_to_uint32(ctx: MuCtx, opnd: MuIntValue): Int = ctx.handleToUInt(opnd).toInt
def handle_to_sint64(ctx: MuCtx, opnd: MuIntValue): Long = ctx.handleToSInt(opnd).toLong
def handle_to_uint64(ctx: MuCtx, opnd: MuIntValue): Long = ctx.handleToUInt(opnd).toLong
def handle_to_float(ctx: MuCtx, opnd: MuFloatValue): Float = ctx.handleToFloat(opnd)
def handle_to_double(ctx: MuCtx, opnd: MuDoubleValue): Double = ctx.handleToDouble(opnd)
def handle_to_ptr(ctx: MuCtx, opnd: MuUPtrValue): MuCPtr = ctx.handleToPtr(opnd)
def handle_to_fp(ctx: MuCtx, opnd: MuUFPValue): MuCFP = ctx.handleToFP(opnd)
def handle_from_const(ctx: MuCtx, id: MuID): MuValuePtr = exposeMuValue(ctx, ctx.handleFromConst(id))
def handle_from_global(ctx: MuCtx, id: MuID): MuValuePtr = exposeMuValue(ctx, ctx.handleFromGlobal(id))
def handle_from_func(ctx: MuCtx, id: MuID): MuValuePtr = exposeMuValue(ctx, ctx.handleFromFunc(id))
def handle_from_expose(ctx: MuCtx, id: MuID): MuValuePtr = exposeMuValue(ctx, ctx.handleFromExpose(id))
/** Need to dispose the value manually. */
def delete_value(ctx: MuCtx, opnd: MuValuePtr): Unit = {
logger.debug("Deleting %d 0x%x".format(opnd, opnd))
val muVal = getMuValueNotNull(opnd)
unexposeMuValue(ctx, opnd)
ctx.deleteValue(muVal)
}
}
/**
* These functions are not exposed.
*/
object NativeMuHelpers {
import NativeClientSupport._
val ONE = BigInt(1)
/**
* Convert unsigned integer to BigInt.
* <p>
* NOTE: Scala implicitly converts Byte, Short, Int or Long to BigInt using sign-extension.
* But when zero-extension is desired (unsigned native arguments), we must truncate the BigInt.
*
* @param len The number of bits in the INPUT number (not the output Mu int).
*/
def trunc(num: BigInt, len: Int): BigInt = num & ((ONE << len) - ONE)
def handleFromInt(ctx: MuCtx, num: BigInt, len: Int): MuValuePtr = {
exposeMuValue(ctx, ctx.handleFromInt(num, len))
}
} }
object ClientAccessibleClassExposer { object ClientAccessibleClassExposer {
...@@ -100,9 +175,9 @@ object ClientAccessibleClassExposer { ...@@ -100,9 +175,9 @@ object ClientAccessibleClassExposer {
def paramFloat(index: Int)(buffer: Buffer): Any = buffer.getFloat(index) def paramFloat(index: Int)(buffer: Buffer): Any = buffer.getFloat(index)
def paramDouble(index: Int)(buffer: Buffer): Any = buffer.getDouble(index) def paramDouble(index: Int)(buffer: Buffer): Any = buffer.getDouble(index)
def paramString(index: Int)(buffer: Buffer): Any = getStr(buffer, index) def paramString(index: Int)(buffer: Buffer): Any = getStr(buffer, index)
def paramMicroVM(index: Int)(buffer: Buffer): Any = getObj(buffer, index, NativeClientSupport.microVMs) def paramMicroVM(index: Int)(buffer: Buffer): Any = getObjFromFuncTableAddr(buffer, index, NativeClientSupport.microVMs)
def paramMuCtx(index: Int)(buffer: Buffer): Any = getObj(buffer, index, NativeClientSupport.muCtxs) def paramMuCtx(index: Int)(buffer: Buffer): Any = getObjFromFuncTableAddr(buffer, index, NativeClientSupport.muCtxs)
def paramMuValue(index: Int)(buffer: Buffer): Any = getObj(buffer, index, NativeClientSupport.muValues) def paramMuValue(index: Int)(buffer: Buffer): Any = getMuValue(buffer, index)
def retVoid(buffer: Buffer, v: Any): Unit = {} def retVoid(buffer: Buffer, v: Any): Unit = {}
def retByte(buffer: Buffer, v: Any): Unit = buffer.setByteReturn(v.asInstanceOf[Byte]) def retByte(buffer: Buffer, v: Any): Unit = buffer.setByteReturn(v.asInstanceOf[Byte])
...@@ -119,6 +194,11 @@ object ClientAccessibleClassExposer { ...@@ -119,6 +194,11 @@ object ClientAccessibleClassExposer {
str str
} }
private def getMuValue(buffer: Buffer, index: Int): MuValue = {
val addr = buffer.getAddress(index)
NativeClientSupport.getMuValueNotNull(addr)
}
private def exposeStr(str: String): Word = { private def exposeStr(str: String): Word = {
val ptr = NativeClientSupport.stringPool.getOrElseUpdate(str, { val ptr = NativeClientSupport.stringPool.getOrElseUpdate(str, {
val bytes = str.getBytes(StandardCharsets.US_ASCII) val bytes = str.getBytes(StandardCharsets.US_ASCII)
...@@ -130,9 +210,9 @@ object ClientAccessibleClassExposer { ...@@ -130,9 +210,9 @@ object ClientAccessibleClassExposer {
ptr.address() ptr.address()
} }
private def getObj[T](buffer: Buffer, index: Int, orm: ObjectReferenceManager[T]): T = { private def getObjFromFuncTableAddr[T](buffer: Buffer, index: Int, orm: ObjectReferenceManager[T]): T = {
val funcTableAddr = buffer.getLong(index) val funcTableAddr = buffer.getLong(index)
NativeClientSupport.getObjNotNull(funcTableAddr, orm) NativeClientSupport.getObjFromFuncTableAddrNotNull(funcTableAddr, orm)
} }
// Reflection utils. // Reflection utils.
...@@ -150,7 +230,7 @@ object ClientAccessibleClassExposer { ...@@ -150,7 +230,7 @@ object ClientAccessibleClassExposer {
case t if t =:= TString => JType.POINTER case t if t =:= TString => JType.POINTER
case t if t =:= TMicroVM => JType.POINTER case t if t =:= TMicroVM => JType.POINTER
case t if t =:= TMuCtx => JType.POINTER case t if t =:= TMuCtx => JType.POINTER
case t if t =:= TMuValue => JType.POINTER case t if t <:< TMuValue => JType.POINTER
} }
/** /**
...@@ -158,16 +238,16 @@ object ClientAccessibleClassExposer { ...@@ -158,16 +238,16 @@ object ClientAccessibleClassExposer {
*/ */
class ExposedMethodClosure(method: ru.MethodMirror, paramGetters: Seq[Buffer => Any], returnSetter: (Buffer, Any) => Unit) extends Closure { class ExposedMethodClosure(method: ru.MethodMirror, paramGetters: Seq[Buffer => Any], returnSetter: (Buffer, Any) => Unit) extends Closure {
def invoke(buffer: Buffer): Unit = { def invoke(buffer: Buffer): Unit = {
val params = paramGetters.map(_(buffer)) try {
val rv = try { val params = paramGetters.map(_(buffer))
method.apply(params: _*) val rv = method.apply(params: _*)
returnSetter(buffer, rv)
} catch { } catch {
case e: Throwable => { case e: Throwable => {
logger.error("Exception thrown before returning to native. This is fatal", e) logger.error("Exception thrown before returning to native. This is fatal", e)
throw e throw e
} }
} }
returnSetter(buffer, rv)
} }
} }
} }
...@@ -201,7 +281,7 @@ class ClientAccessibleClassExposer[T: ru.TypeTag: ClassTag](obj: T) { ...@@ -201,7 +281,7 @@ class ClientAccessibleClassExposer[T: ru.TypeTag: ClassTag](obj: T) {
case t if t =:= TString => paramString(i) _ case t if t =:= TString => paramString(i) _
case t if t =:= TMicroVM => paramMicroVM(i) _ case t if t =:= TMicroVM => paramMicroVM(i) _
case t if t =:= TMuCtx => paramMuCtx(i) _ case t if t =:= TMuCtx => paramMuCtx(i) _
case t if t =:= TMuValue => paramMuValue(i) _ case t if t <:< TMuValue => paramMuValue(i) _
} }
val returnSetter = returnType match { val returnSetter = returnType match {
...@@ -259,17 +339,44 @@ class ClientAccessibleClassExposer[T: ru.TypeTag: ClassTag](obj: T) { ...@@ -259,17 +339,44 @@ class ClientAccessibleClassExposer[T: ru.TypeTag: ClassTag](obj: T) {
/** /**
* Support for native clients (such as C programs). * Support for native clients (such as C programs).
* <p>
* MicroVM, MuCtx and MuValue instances can be exposed to native programs.
* <p>
* An exposed MicroVM has a function table (the MuVM struct in C) and a fake pointer (managed by the
* ObjectReferenceManager) referring to the Scala MicroVM instance. The former is kept in the funcTableToPtr map,
* and the latter is kept in the microVMs ORM. The fake pointer is stored in the header field of the MuVM struct.
* <p>
* An exposed MuCtx has a function table, a fake pointer, and a hash set containing all exposed MuValues derived from
* that MuCtx, held in muCtxToMuValue(theCtx). Unexposing a MuCtx automatically unexposes all of its MuValues. The fake
* pointer is the header of the MuCtx struct.
* <p>
* An exposed MuValue only has a fake pointer, held in muValues. The pointer is also stored in muCtxToMuValues(ctx) for
* its context ctx.
*/ */
object NativeClientSupport { object NativeClientSupport {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
// Syntax sugar for MuCtx and MuValue return values, which are pointers
type FuncTablePtr = Word
type MuVMPtr = FuncTablePtr
type MuCtxPtr = FuncTablePtr
type MuValuePtr = Word
type MuID = Int
type MuName = String
type MuCPtr = Word
type MuCFP = Word
// Give exposed objects a random "memory address" so native programs can pass them back to Mu as parameters. // Give exposed objects a random "memory address" so native programs can pass them back to Mu as parameters.
val microVMs = jnrRuntime.newObjectReferenceManager[MicroVM]() val microVMs = jnrRuntime.newObjectReferenceManager[MicroVM]()
val muCtxs = jnrRuntime.newObjectReferenceManager[MuCtx]() val muCtxs = jnrRuntime.newObjectReferenceManager[MuCtx]()
val muValues = jnrRuntime.newObjectReferenceManager[MuValue]() val muValues = jnrRuntime.newObjectReferenceManager[MuValue]()
/** Map each MuCtx to all of its current MuValues. This is needed when closing a MuCtx. */ /** Map each MuCtx to all of its current MuValues. This is needed when closing a MuCtx. */
val muCtxToMuValues = HashMap[MuCtx, HashSet[Word]]() val muCtxToMuValues = HashMap[MuCtx, HashSet[MuValuePtr]]()
def getObjNotNull[T](funcTableAddr: Word, orm: ObjectReferenceManager[T]): T = { /** Given a function table pointer, get an object from a ObjectReferenceManager. Assert it is not null. */
def getObjFromFuncTableAddrNotNull[T](funcTableAddr: FuncTablePtr, orm: ObjectReferenceManager[T]): T = {
val objAddr = theMemory.getAddress(funcTableAddr) val objAddr = theMemory.getAddress(funcTableAddr)
val obj = orm.get(jnrMemoryManager.newPointer(objAddr)) val obj = orm.get(jnrMemoryManager.newPointer(objAddr))
if (obj == null) { if (obj == null) {
...@@ -278,11 +385,20 @@ object NativeClientSupport { ...@@ -278,11 +385,20 @@ object NativeClientSupport {
obj obj
} }
/** Get the MuValue instance form a C MuValue (a pointer). */
def getMuValueNotNull(addr: MuValuePtr): MuValue = {
val muVal = muValues.get(jnrMemoryManager.newPointer(addr))
if (muVal == null) {
throw new UvmRefImplException("Exposed MuValue not found. Address: %d 0x%x".format(addr, addr))
}
muVal
}
/** /**
* Map function table addresses to Pointer so they can be closed. JFFI uses the TrantientNativeMemory (wrapped * Map function table addresses to Pointer so they can be closed. JFFI uses the TrantientNativeMemory (wrapped
* in the Pointer object) to keep the allocated native memory alive. * in the Pointer object) to keep the allocated native memory alive.
*/ */
val funcTableToPointer = HashMap[Word, Pointer]() val funcTableToPointer = HashMap[FuncTablePtr, Pointer]()
/** /**
* Map Java strings (currently only names of Identified objects in MU) to Pointers to keep the allocated strings * Map Java strings (currently only names of Identified objects in MU) to Pointers to keep the allocated strings
...@@ -295,12 +411,14 @@ object NativeClientSupport { ...@@ -295,12 +411,14 @@ object NativeClientSupport {
val muCtxExposer = new ClientAccessibleClassExposer(NativeMuCtx) val muCtxExposer = new ClientAccessibleClassExposer(NativeMuCtx)
// Expose and unexpose objects // Expose and unexpose objects
/** Expose a MicroVM. Return a pointer to the C MuVM structure. */ /** Expose a MicroVM. Return a pointer to the C MuVM structure. */
def exposeMicroVM(microVM: MicroVM): Word = { def exposeMicroVM(microVM: MicroVM): Word = {
val objAddr = microVMs.add(microVM).address() val objAddr = microVMs.add(microVM).address()
val funcTableAddr = muVMExposer.makeFuncTable(objAddr) val funcTableAddr = muVMExposer.makeFuncTable(objAddr)
funcTableAddr funcTableAddr
} }
/** Expose a MuCtx. Return a pointer to the C MuCtx structure. */ /** Expose a MuCtx. Return a pointer to the C MuCtx structure. */
def exposeMuCtx(muCtx: MuCtx): Word = { def exposeMuCtx(muCtx: MuCtx): Word = {
require(!muCtxToMuValues.contains(muCtx), "Context %s is already exposed.".format(muCtx)) require(!muCtxToMuValues.contains(muCtx), "Context %s is already exposed.".format(muCtx))
...@@ -309,27 +427,43 @@ object NativeClientSupport { ...@@ -309,27 +427,43 @@ object NativeClientSupport {
val funcTableAddr = muCtxExposer.makeFuncTable(objAddr) val funcTableAddr = muCtxExposer.makeFuncTable(objAddr)
funcTableAddr funcTableAddr
} }
/** Expose a MuValue. Return an "object pointer", i.e. faked by ObjectReferenceManager. */ /** Expose a MuValue. Return an "object pointer", i.e. faked by ObjectReferenceManager. */
def exposeMuValue(muValue: MuValue): Word = muValues.add(muValue).address() def exposeMuValue(muCtx: MuCtx, muValue: MuValue): Word = {
val addr = muValues.add(muValue).address()
muCtxToMuValues(muCtx).add(addr)
addr
}
/** Unexpose a MicroVM. Remove the faked pointer and deallocate the function table (by removing the Pointer). */ /** Unexpose a MicroVM. Remove the faked pointer and deallocate the function table (by removing the Pointer). */
def unexposeMicroVM(funcTableAddr: Word): Unit = { def unexposeMicroVM(funcTableAddr: Word): Unit = {
require(funcTableToPointer.contains(funcTableAddr), require(funcTableToPointer.contains(funcTableAddr),
"MuVM struct pointer %d 0x%x does not exist".format(funcTableAddr, funcTableAddr)) "MuVM struct pointer %d 0x%x does not exist".format(funcTableAddr, funcTableAddr))
val obj = getObjNotNull(funcTableAddr, microVMs) val objPtr = theMemory.getPointer(funcTableAddr)
val objAddr = theMemory.getAddress(funcTableAddr) microVMs.remove(objPtr).ensuring(_ == true)
microVMs.remove(jnrMemoryManager.newPointer(objAddr))
funcTableToPointer.remove(funcTableAddr) funcTableToPointer.remove(funcTableAddr)
} }
/** Unexpose a MuCtx. Remove the faked pointer and deallocate the function table (by removing the Pointer). */
/**
* Unexpose a MuCtx. Remove the faked pointer and deallocate the function table (by removing the Pointer).
* Also unexpose all MuValues held by the MuCtx.
*/
def unexposeMuCtx(funcTableAddr: Long): Unit = { def unexposeMuCtx(funcTableAddr: Long): Unit = {
require(funcTableToPointer.contains(funcTableAddr), require(funcTableToPointer.contains(funcTableAddr),
"MuCtx struct pointer %d 0x%x does not exist".format(funcTableAddr, funcTableAddr)) "MuCtx struct pointer %d 0x%x does not exist".format(funcTableAddr, funcTableAddr))
val obj = getObjNotNull(funcTableAddr, muCtxs) val objPtr = theMemory.getPointer(funcTableAddr)
val objAddr = theMemory.getAddress(funcTableAddr) val muCtx = muCtxs.get(objPtr).ensuring { _ != null }
muCtxs.remove(jnrMemoryManager.newPointer(objAddr)) for (muValueAddr <- muCtxToMuValues.remove(muCtx).ensuring(_.isDefined).get) {
// Don't remove it from muCtxToMuValues(muCtx) because the whole HashSet has just been detached.
muValues.remove(jnrMemoryManager.newPointer(muValueAddr)).ensuring(_ == true)
}
muCtxs.remove(objPtr)
funcTableToPointer.remove(funcTableAddr) funcTableToPointer.remove(funcTableAddr)
} }
/** Unexpose a MuValue by removing it from the ORM. */ /** Unexpose a MuValue by removing it from the ORM. */
def unexposeMuValue(addr: Long): Unit = muValues.remove(jnrMemoryManager.newPointer(addr)) def unexposeMuValue(muCtx: MuCtx, addr: Long): Unit = {
muCtxToMuValues(muCtx).remove(addr)
muValues.remove(jnrMemoryManager.newPointer(addr)).ensuring(_ == true)
}
} }
\ No newline at end of file
...@@ -12,24 +12,35 @@ class NativeClientSupportTest extends UvmBundleTesterBase { ...@@ -12,24 +12,35 @@ class NativeClientSupportTest extends UvmBundleTesterBase {
ROOT_LOGGER_NAME -> INFO, ROOT_LOGGER_NAME -> INFO,
"uvm.refimpl.nat" -> DEBUG) "uvm.refimpl.nat" -> DEBUG)
preloadBundles("tests/uvm-refimpl-test/primitives.uir") preloadBundles("tests/uvm-refimpl-test/primitives.uir",
"tests/uvm-refimpl-test/native-client-test.uir")
val fileName = "./tests/c-snippets/ncs_tests.so" val fileName = "./tests/c-snippets/ncs_tests.so"
if (!new java.io.File(fileName).isFile()) { if (!new java.io.File(fileName).isFile()) {
throw new RuntimeException("Need to compile the ncs_tests.so library. cd into tests/c-snippets and invoke 'make'.") throw new RuntimeException("Need to compile the ncs_tests.so library. cd into tests/c-snippets and invoke 'make'.")
} }
type CBool = Byte
trait NcsTestsLib { trait NcsTestsLib {
def test_basic(mvm: Word, theID: Int, theName: String): Int def test_basic(mvm: Word, theID: Int, theName: String): CBool
def test_with_ctx(mvm: Word, theID: Int, theName: String): Int def test_with_ctx(mvm: Word, theID: Int, theName: String): CBool
def test_basic_conv(mvm: Word): CBool
def test_global_vars(mvm: Word, the_plus_one_fp: Word): CBool
} }
val ncs_tests = LibraryLoader.create(classOf[NcsTestsLib]).load(fileName) val ncs_tests = LibraryLoader.create(classOf[NcsTestsLib]).load(fileName)
val microVMFuncTableAddr = NativeClientSupport.exposeMicroVM(microVM) val microVMFuncTableAddr = NativeClientSupport.exposeMicroVM(microVM)
def assertNativeSuccess(result: CBool): Unit = {
if (result == 0) {
fail("Failed in the native program.")
}
}
behavior of "The ClientAccessibleClassExposer" behavior of "The ClientAccessibleClassExposer"