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.4% 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(
scalaVersion := "2.11.7",
libraryDependencies ++= Seq(
"org.scala-lang" % "scala-reflect" % "2.11.7",
"org.antlr" % "antlr4" % "4.5.1-1",
"com.typesafe.scala-logging" %% "scala-logging" % "3.1.0",
"ch.qos.logback" % "logback-classic" % "1.1.3",
......
......@@ -134,13 +134,13 @@ struct MuCtx {
void (*load_hail )(MuCtx *ctx, char *buf, int sz);
// 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_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_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_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);
MuFloatValue (*handle_from_float )(MuCtx *ctx, float num);
MuDoubleValue (*handle_from_double)(MuCtx *ctx, double num);
......
package uvm.refimpl.nat
import java.nio.charset.StandardCharsets
import scala.collection.mutable.HashMap
import scala.collection.mutable.HashSet
import scala.reflect.ClassTag
import scala.reflect.runtime.universe
import scala.reflect.runtime.{ universe => ru }
import org.slf4j.LoggerFactory
import com.kenai.jffi.CallingConvention
import com.kenai.jffi.Closure
import com.kenai.jffi.Closure.Buffer
import com.kenai.jffi.{ Type => JType }
import com.typesafe.scalalogging.Logger
import NativeSupport._
import PlatformConstants.WORD_SIZE_BYTES
import PlatformConstants.Word
import jnr.ffi.ObjectReferenceManager
import jnr.ffi.Pointer
import uvm.refimpl._
import scala.collection.mutable.HashSet
/**
* This object calls a C function to handle the trap.
......@@ -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.
*/
object NativeMuVM {
// Syntax sugar for MuCtx and MuValue return values, which are pointers
type MuCtxPtr = Word
type MuValuePtr = Word
import NativeMuHelpers._
import NativeClientSupport._
def new_context(microVM: MicroVM): MuCtxPtr = {
val ctx = microVM.newContext()
val ptr = NativeClientSupport.exposeMuCtx(ctx)
val ptr = exposeMuCtx(ctx)
ptr
}
def id_of(microVM: MicroVM, name: String): Int = microVM.idOf(name)
def name_of(microVM: MicroVM, id: Int): String = microVM.nameOf(id)
def id_of(microVM: MicroVM, name: MuName): MuID = microVM.idOf(name)
def name_of(microVM: MicroVM, id: MuID): MuName = microVM.nameOf(id)
def set_trap_handler(microVM: MicroVM, trap_handler: Word, userdata: Word): Unit = {
microVM.setTrapHandler(new NativeTrapHandler(trap_handler, userdata))
}
......@@ -50,27 +53,99 @@ object NativeMuVM {
/**
* 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 {
// Syntax sugar for MuCtx and MuValue return values, which are pointers
type MuCtxPtr = Word
type MuValuePtr = Word
import NativeMuHelpers._
import NativeClientSupport._
def id_of(ctx: MuCtx, name: String): Int = ctx.idOf(name)
def name_of(ctx: MuCtx, id: Int): String = ctx.nameOf(id)
def id_of(ctx: MuCtx, name: MuName): MuID = ctx.idOf(name)
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
* the function table, too.
*/
def close_context(funcTableAddr: Word): Unit = {
val ctx = NativeClientSupport.getObjNotNull(funcTableAddr, NativeClientSupport.muCtxs)
for (muValueAddr <- NativeClientSupport.muCtxToMuValues.remove(ctx).get) {
NativeClientSupport.unexposeMuValue(muValueAddr)
}
NativeClientSupport.unexposeMuCtx(funcTableAddr)
val ctx = getObjFromFuncTableAddrNotNull(funcTableAddr, NativeClientSupport.muCtxs)
unexposeMuCtx(funcTableAddr)
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 {
......@@ -100,9 +175,9 @@ object ClientAccessibleClassExposer {
def paramFloat(index: Int)(buffer: Buffer): Any = buffer.getFloat(index)
def paramDouble(index: Int)(buffer: Buffer): Any = buffer.getDouble(index)
def paramString(index: Int)(buffer: Buffer): Any = getStr(buffer, index)
def paramMicroVM(index: Int)(buffer: Buffer): Any = getObj(buffer, index, NativeClientSupport.microVMs)
def paramMuCtx(index: Int)(buffer: Buffer): Any = getObj(buffer, index, NativeClientSupport.muCtxs)
def paramMuValue(index: Int)(buffer: Buffer): Any = getObj(buffer, index, NativeClientSupport.muValues)
def paramMicroVM(index: Int)(buffer: Buffer): Any = getObjFromFuncTableAddr(buffer, index, NativeClientSupport.microVMs)
def paramMuCtx(index: Int)(buffer: Buffer): Any = getObjFromFuncTableAddr(buffer, index, NativeClientSupport.muCtxs)
def paramMuValue(index: Int)(buffer: Buffer): Any = getMuValue(buffer, index)
def retVoid(buffer: Buffer, v: Any): Unit = {}
def retByte(buffer: Buffer, v: Any): Unit = buffer.setByteReturn(v.asInstanceOf[Byte])
......@@ -119,6 +194,11 @@ object ClientAccessibleClassExposer {
str
}
private def getMuValue(buffer: Buffer, index: Int): MuValue = {
val addr = buffer.getAddress(index)
NativeClientSupport.getMuValueNotNull(addr)
}
private def exposeStr(str: String): Word = {
val ptr = NativeClientSupport.stringPool.getOrElseUpdate(str, {
val bytes = str.getBytes(StandardCharsets.US_ASCII)
......@@ -130,9 +210,9 @@ object ClientAccessibleClassExposer {
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)
NativeClientSupport.getObjNotNull(funcTableAddr, orm)
NativeClientSupport.getObjFromFuncTableAddrNotNull(funcTableAddr, orm)
}
// Reflection utils.
......@@ -150,7 +230,7 @@ object ClientAccessibleClassExposer {
case t if t =:= TString => JType.POINTER
case t if t =:= TMicroVM => 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 {
*/
class ExposedMethodClosure(method: ru.MethodMirror, paramGetters: Seq[Buffer => Any], returnSetter: (Buffer, Any) => Unit) extends Closure {
def invoke(buffer: Buffer): Unit = {
val params = paramGetters.map(_(buffer))
val rv = try {
method.apply(params: _*)
try {
val params = paramGetters.map(_(buffer))
val rv = method.apply(params: _*)
returnSetter(buffer, rv)
} catch {
case e: Throwable => {
logger.error("Exception thrown before returning to native. This is fatal", e)
throw e
}
}
returnSetter(buffer, rv)
}
}
}
......@@ -201,7 +281,7 @@ class ClientAccessibleClassExposer[T: ru.TypeTag: ClassTag](obj: T) {
case t if t =:= TString => paramString(i) _
case t if t =:= TMicroVM => paramMicroVM(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 {
......@@ -259,17 +339,44 @@ class ClientAccessibleClassExposer[T: ru.TypeTag: ClassTag](obj: T) {
/**
* 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 {
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.
val microVMs = jnrRuntime.newObjectReferenceManager[MicroVM]()
val muCtxs = jnrRuntime.newObjectReferenceManager[MuCtx]()
val muValues = jnrRuntime.newObjectReferenceManager[MuValue]()
/** 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 obj = orm.get(jnrMemoryManager.newPointer(objAddr))
if (obj == null) {
......@@ -278,11 +385,20 @@ object NativeClientSupport {
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
* 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
......@@ -295,12 +411,14 @@ object NativeClientSupport {
val muCtxExposer = new ClientAccessibleClassExposer(NativeMuCtx)
// Expose and unexpose objects
/** Expose a MicroVM. Return a pointer to the C MuVM structure. */
def exposeMicroVM(microVM: MicroVM): Word = {
val objAddr = microVMs.add(microVM).address()
val funcTableAddr = muVMExposer.makeFuncTable(objAddr)
funcTableAddr
}
/** Expose a MuCtx. Return a pointer to the C MuCtx structure. */
def exposeMuCtx(muCtx: MuCtx): Word = {
require(!muCtxToMuValues.contains(muCtx), "Context %s is already exposed.".format(muCtx))
......@@ -309,27 +427,43 @@ object NativeClientSupport {
val funcTableAddr = muCtxExposer.makeFuncTable(objAddr)
funcTableAddr
}
/** 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). */
def unexposeMicroVM(funcTableAddr: Word): Unit = {
require(funcTableToPointer.contains(funcTableAddr),
"MuVM struct pointer %d 0x%x does not exist".format(funcTableAddr, funcTableAddr))
val obj = getObjNotNull(funcTableAddr, microVMs)
val objAddr = theMemory.getAddress(funcTableAddr)
microVMs.remove(jnrMemoryManager.newPointer(objAddr))
val objPtr = theMemory.getPointer(funcTableAddr)
microVMs.remove(objPtr).ensuring(_ == true)
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 = {
require(funcTableToPointer.contains(funcTableAddr),
"MuCtx struct pointer %d 0x%x does not exist".format(funcTableAddr, funcTableAddr))
val obj = getObjNotNull(funcTableAddr, muCtxs)
val objAddr = theMemory.getAddress(funcTableAddr)
muCtxs.remove(jnrMemoryManager.newPointer(objAddr))
val objPtr = theMemory.getPointer(funcTableAddr)
val muCtx = muCtxs.get(objPtr).ensuring { _ != null }
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)
}
/** 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 {
ROOT_LOGGER_NAME -> INFO,
"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"
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'.")
}
type CBool = Byte
trait NcsTestsLib {
def test_basic(mvm: Word, theID: Int, theName: String): Int
def test_with_ctx(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): 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 microVMFuncTableAddr = NativeClientSupport.exposeMicroVM(microVM)
def assertNativeSuccess(result: CBool): Unit = {
if (result == 0) {
fail("Failed in the native program.")
}
}
behavior of "The ClientAccessibleClassExposer"
it should "be able to access the exposed MicroVM" in {
val funcTablePtr = jnrMemoryManager.newPointer(microVMFuncTableAddr)
val header = funcTablePtr.getAddress(0)
......@@ -41,20 +52,29 @@ class NativeClientSupportTest extends UvmBundleTesterBase {
val theID = microVM.idOf(theName)
val result = ncs_tests.test_basic(microVMFuncTableAddr, theID, theName)
if (result == 0) {
fail("Failed in the native program.")
}
assertNativeSuccess(result)
}
it should "be able to create MuCtx and use it" in {
val funcTablePtr = jnrMemoryManager.newPointer(microVMFuncTableAddr)
it should "be able to create MuCtx and use it" in {
val theName = "@double"
val theID = microVM.idOf(theName)
val result = ncs_tests.test_with_ctx(microVMFuncTableAddr, theID, theName)
if (result == 0) {
fail("Failed in the native program.")
}
assertNativeSuccess(result)
}
it should "convert C types to Mu types and back, with the same value" in {
val result = ncs_tests.test_basic_conv(microVMFuncTableAddr)
assertNativeSuccess(result)
}
it should "get values from global variables" in {
val ctx = microVM.newContext()
val hThePlusOneFP = ctx.handleFromExpose(ctx.idOf("@plus_one_native"))
val thePlusOneFP = ctx.handleToFP(hThePlusOneFP)
ctx.closeContext()
val result = ncs_tests.test_global_vars(microVMFuncTableAddr, thePlusOneFP)
assertNativeSuccess(result)
}
}
\ No newline at end of file
......@@ -4,10 +4,10 @@ MU_INCLUDE = ../../cinclude
all: structtest.so callbacktest.so ncs_tests.so
structtest.so: structtest.c
cc -fPIC -shared -o structtest.so structtest.c
cc -std=c11 -fPIC -shared -o structtest.so structtest.c
callbacktest.so: callbacktest.c
cc -fPIC -shared -o callbacktest.so callbacktest.c
cc -std=c11 -fPIC -shared -o callbacktest.so callbacktest.c
ncs_tests.so: ncs_tests.c
cc -fPIC -shared -I $(MU_INCLUDE) -o ncs_tests.so ncs_tests.c
cc -std=c11 -fPIC -shared -I $(MU_INCLUDE) -o ncs_tests.so ncs_tests.c
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
#include <muapi.h>
int test_basic(MuVM *mvm, int theID, char *theName) {
printf("[C] header: %p\n", mvm->header);
printf("[C] new_context: %p\n", mvm->new_context);
printf("[C] id_of: %p\n", mvm->id_of);
printf("[C] name_of: %p\n", mvm->name_of);
printf("[C] set_trap_handler: %p\n", mvm->set_trap_handler);
#define MU_ASSERT_EQUALS(a, b, f) if (!(a == b)) { \
muprintf("%s (%" f ") is not equal to %s (%" f ")\n", #a, a, #b, b); \
return false; \
}
#define ID(name) ctx->id_of(ctx, name)
#define muprintf(fmt, ...) printf("[C:%d:%s] " fmt, __LINE__, __func__, ## __VA_ARGS__)
bool test_basic(MuVM *mvm, int theID, char *theName) {
muprintf("header: %p\n", mvm->header);
muprintf("new_context: %p\n", mvm->new_context);
muprintf("id_of: %p\n", mvm->id_of);
muprintf("name_of: %p\n", mvm->name_of);
muprintf("set_trap_handler: %p\n", mvm->set_trap_handler);
int id = mvm->id_of(mvm, theName);
printf("[C] id = %d\n", id);
muprintf("id = %d\n", id);
if (id != theID) {
printf("[C] ID %d is not equal to %d\n", id, theID);
return 0;
muprintf("ID %d is not equal to %d\n", id, theID);
return false;
}
char *name = mvm->name_of(mvm, theID);
printf("[C] name = %s\n", name);
muprintf("name = %s\n", name);
if (strcmp(name, theName) != 0) {
printf("[C] name %s is not equal to %s\n", name, theName);
return 0;
muprintf("name %s is not equal to %s\n", name, theName);
return false;
}
return 1;
return true;
}
int test_with_ctx(MuVM *mvm, int theID, char *theName) {
bool test_with_ctx(MuVM *mvm, int theID, char *theName) {
MuCtx *ctx = mvm->new_context(mvm);
int id = ctx->id_of(ctx, theName);
printf("[C] id = %d\n", id);
muprintf("id = %d\n", id);
if (id != theID) {
printf("[C] ID %d is not equal to %d\n", id, theID);
return 0;
muprintf("ID %d is not equal to %d\n", id, theID);
return false;
}
char *name = ctx->name_of(ctx, theID);
printf("[C] name = %s\n", name);
muprintf("name = %s\n", name);
if (strcmp(name, theName) != 0) {
printf("[C] name %s is not equal to %s\n", name, theName);
return 0;
muprintf("name %s is not equal to %s\n", name, theName);
return false;
}
ctx->close_context(ctx);
return true;
}
bool test_basic_conv(MuVM *mvm) {