Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
M
mu-impl-ref2
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
5
Issues
5
List
Boards
Labels
Milestones
Merge Requests
2
Merge Requests
2
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
mu
mu-impl-ref2
Commits
64c681d8
Commit
64c681d8
authored
Dec 01, 2015
by
Kunshan Wang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
More MuCtx methods and tests.
A C client can now call API functions to convert between primitive C values and Mu handles.
parent
607995f8
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
407 additions
and
82 deletions
+407
-82
build.sbt
build.sbt
+1
-0
cinclude/muapi.h
cinclude/muapi.h
+4
-4
src/main/scala/uvm/refimpl/nat/nativeClientSupport.scala
src/main/scala/uvm/refimpl/nat/nativeClientSupport.scala
+175
-41
src/test/scala/uvm/refimpl/nat/NativeClientSupportTest.scala
src/test/scala/uvm/refimpl/nat/NativeClientSupportTest.scala
+33
-13
tests/c-snippets/Makefile
tests/c-snippets/Makefile
+3
-3
tests/c-snippets/ncs_tests.c
tests/c-snippets/ncs_tests.c
+180
-21
tests/uvm-refimpl-test/native-client-test.uir
tests/uvm-refimpl-test/native-client-test.uir
+11
-0
No files found.
build.sbt
View file @
64c681d8
...
...
@@ -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"
,
...
...
cinclude/muapi.h
View file @
64c681d8
...
...
@@ -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
);
...
...
src/main/scala/uvm/refimpl/nat/nativeClientSupport.scala
View file @
64c681d8
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
=
get
Obj
(
buffer
,
index
,
NativeClientSupport
.
muValues
)
def
paramMicroVM
(
index
:
Int
)(
buffer
:
Buffer
)
:
Any
=
getObj
FromFuncTableAddr
(
buffer
,
index
,
NativeClientSupport
.
microVMs
)
def
paramMuCtx
(
index
:
Int
)(
buffer
:
Buffer
)
:
Any
=
getObj
FromFuncTableAddr
(
buffer
,
index
,
NativeClientSupport
.
muCtxs
)
def
paramMuValue
(
index
:
Int
)(
buffer
:
Buffer
)
:
Any
=
get
MuValue
(
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
getObj
FromFuncTableAddr
[
T
](
buffer
:
Buffer
,
index
:
Int
,
orm
:
ObjectReferenceManager
[
T
])
:
T
=
{
val
funcTableAddr
=
buffer
.
getLong
(
index
)
NativeClientSupport
.
getObjNotNull
(
funcTableAddr
,
orm
)
NativeClientSupport
.
getObj
FromFuncTableAddr
NotNull
(
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
src/test/scala/uvm/refimpl/nat/NativeClientSupportTest.scala
View file @
64c681d8
...
...
@@ -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
tests/c-snippets/Makefile
View file @
64c681d8
...
...
@@ -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
tests/c-snippets/ncs_tests.c
View file @
64c681d8
#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
)
{
MuCtx
*
ctx
=
mvm
->
new_context
(
mvm
);
int8_t
p8
=
0x7f
;
int8_t
n8
=
-
0x80
;
uint8_t
u8
=
0xffU
;
uint8_t
r8
=
0x5aU
;
int16_t
p16
=
0x7fff
;
int16_t
n16
=
-
0x8000
;
uint16_t
u16
=
0xffffU
;
uint16_t
r16
=
0x55aaU
;
int32_t
p32
=
0x7fffffff
;
int32_t
n32
=
-
0x80000000
;
uint32_t
u32
=
0xffffffffU
;
uint32_t
r32
=
0x5555aaaaU
;
int64_t
p64
=
0x7fffffffffffffffL
;
int64_t
n64
=
-
0x8000000000000000L
;
uint64_t
u64
=
0xffffffffffffffffUL
;
uint64_t
r64
=
0x55555555aaaaaaaaUL
;
MuIntValue
vp8
=
ctx
->
handle_from_sint8
(
ctx
,
p8
,
8
);
MuIntValue
vn8
=
ctx
->
handle_from_sint8
(
ctx
,
n8
,
8
);
MuIntValue
vu8
=
ctx
->
handle_from_uint8
(
ctx
,
u8
,
8
);
MuIntValue
vr8
=
ctx
->
handle_from_uint8
(
ctx
,
r8
,
8
);
MuIntValue
vp16
=
ctx
->
handle_from_sint16
(
ctx
,
p16
,
16
);
MuIntValue
vn16
=
ctx
->
handle_from_sint16
(
ctx
,
n16
,
16
);
MuIntValue
vu16
=
ctx
->
handle_from_uint16
(
ctx
,
u16
,
16
);
MuIntValue
vr16
=
ctx
->
handle_from_uint16
(
ctx
,
r16
,
16
);
MuIntValue
vp32
=
ctx
->
handle_from_sint32
(
ctx
,
p32
,
32
);
MuIntValue
vn32
=
ctx
->
handle_from_sint32
(
ctx
,
n32
,
32
);
MuIntValue
vu32
=
ctx
->
handle_from_uint32
(
ctx
,
u32
,
32
);
MuIntValue
vr32
=
ctx
->
handle_from_uint32
(
ctx
,
r32
,
32
);
MuIntValue
vp64
=
ctx
->
handle_from_sint64
(
ctx
,
p64
,
64
);
MuIntValue
vn64
=
ctx
->
handle_from_sint64
(
ctx
,
n64
,
64
);
MuIntValue
vu64
=
ctx
->
handle_from_uint64
(
ctx
,
u64
,
64
);
MuIntValue
vr64
=
ctx
->
handle_from_uint64
(
ctx
,
r64
,
64
);
int8_t
bp8
=
ctx
->
handle_to_sint8
(
ctx
,
vp8
);
int8_t
bn8
=
ctx
->
handle_to_sint8
(
ctx
,
vn8
);
uint8_t
bu8
=
ctx
->
handle_to_uint8
(
ctx
,
vu8
);
uint8_t
br8
=
ctx
->
handle_to_uint8
(
ctx
,
vr8
);
int16_t
bp16
=
ctx
->
handle_to_sint16
(
ctx
,
vp16
);
int16_t
bn16
=
ctx
->
handle_to_sint16
(
ctx
,
vn16
);
uint16_t
bu16
=
ctx
->
handle_to_uint16
(
ctx
,
vu16
);
uint16_t
br16
=
ctx
->
handle_to_uint16
(
ctx
,
vr16
);
int32_t
bp32
=
ctx
->
handle_to_sint32
(
ctx
,
vp32
);
int32_t
bn32
=
ctx
->
handle_to_sint32
(
ctx
,
vn32
);
uint32_t
bu32
=
ctx
->
handle_to_uint32
(
ctx
,
vu32
);
uint32_t
br32
=
ctx
->
handle_to_uint32
(
ctx
,
vr32
);
int64_t
bp64
=
ctx
->
handle_to_sint64
(
ctx
,
vp64
);
int64_t
bn64
=
ctx
->
handle_to_sint64
(
ctx
,
vn64
);
uint64_t
bu64
=
ctx
->
handle_to_uint64
(
ctx
,
vu64
);
uint64_t
br64
=
ctx
->
handle_to_uint64
(
ctx
,
vr64
);
MU_ASSERT_EQUALS
(
p8
,
bp8
,
PRIi8
)
MU_ASSERT_EQUALS
(
n8
,
bn8
,
PRIi8
)
MU_ASSERT_EQUALS
(
u8
,
bu8
,
PRIu8
)
MU_ASSERT_EQUALS
(
r8
,
br8
,
PRIu8
)
MU_ASSERT_EQUALS
(
p16
,
bp16
,
PRIi16
)
MU_ASSERT_EQUALS
(
n16
,
bn16
,
PRIi16
)
MU_ASSERT_EQUALS
(
u16
,
bu16
,
PRIu16
)
MU_ASSERT_EQUALS
(
r16
,
br16
,
PRIu16
)
MU_ASSERT_EQUALS
(
p32
,
bp32
,
PRIi32
)
MU_ASSERT_EQUALS
(
n32
,
bn32
,
PRIi32
)
MU_ASSERT_EQUALS
(
u32
,
bu32
,
PRIu32
)
MU_ASSERT_EQUALS
(
r32
,
br32
,
PRIu32
)
MU_ASSERT_EQUALS
(
p64
,
bp64
,
PRIi64
)
MU_ASSERT_EQUALS
(
n64
,
bn64
,
PRIi64
)
MU_ASSERT_EQUALS
(
u64
,
bu64
,
PRIu64
)
MU_ASSERT_EQUALS
(
r64
,
br64
,
PRIu64
)
MuIntValue
sep8t64
=
ctx
->
handle_from_sint8
(
ctx
,
p8
,
64
);
MuIntValue
sen8t64
=
ctx
->
handle_from_sint8
(
ctx
,
n8
,
64
);
MuIntValue
zeu8t64
=
ctx
->
handle_from_uint8
(
ctx
,
u8
,
64
);
MuIntValue
zer8t64
=
ctx
->
handle_from_uint8
(
ctx
,
r8
,
64
);
uint64_t
bsep8t64
=
ctx
->
handle_to_uint64
(
ctx
,
sep8t64
);
uint64_t
bsen8t64
=
ctx
->
handle_to_uint64
(
ctx
,
sen8t64
);
uint64_t
bzeu8t64
=
ctx
->
handle_to_uint64
(
ctx
,
zeu8t64
);
uint64_t
bzer8t64
=
ctx
->
handle_to_uint64
(
ctx
,
zer8t64
);
MU_ASSERT_EQUALS
(
bsep8t64
,
0x000000000000007fULL
,
PRIx64
);
MU_ASSERT_EQUALS
(
bsen8t64
,
0xffffffffffffff80ULL
,
PRIx64
);
MU_ASSERT_EQUALS
(
bzeu8t64
,
0x00000000000000ffULL
,
PRIx64
);
MU_ASSERT_EQUALS
(
bzer8t64
,
0x000000000000005aULL
,
PRIx64
);
float
f
=
3
.
14
f
;
double
d
=
6
.
28
;
MuFloatValue
hf
=
ctx
->
handle_from_float
(
ctx
,
f
);
MuDoubleValue
hd
=
ctx
->
handle_from_double
(
ctx
,
d
);