Commit 8a7d6825 authored by Kunshan Wang's avatar Kunshan Wang

WIP: Testing python binding...

parent e00ea518
......@@ -36,7 +36,7 @@ def _assert_instance(obj, *tys):
#### Low-level C types counterpart
def _funcptr(restype, *paramtypes, **kwargs):
return ctypes.CFUNCTYPE(restype, *paramtypes, **kwargs)
return ctypes.CFUNCTYPE(restype, *paramtypes, use_errno=True, **kwargs)
CMuValue = ctypes.c_void_p
CMuGenRefValue = CMuValue
......@@ -67,9 +67,9 @@ CMuCFP = ctypes.c_void_p
CMuTrapHandlerResult = ctypes.c_int
CMuHowToResume = ctypes.c_int
C_MU_THREAD_EXIT = 0x00
C_MU_REBIND_PASS_VALUES = 0x01
C_MU_REBIND_THROW_EXC = 0x02
MU_THREAD_EXIT = 0x00
MU_REBIND_PASS_VALUES = 0x01
MU_REBIND_THROW_EXC = 0x02
CPtrMuValue = ctypes.POINTER(CMuValue)
......@@ -111,31 +111,31 @@ CMuTrapHandler = _funcptr(
CMuMemOrd = ctypes.c_int
C_MU_NOT_ATOMIC = 0x00
C_MU_RELAXED = 0x01
C_MU_CONSUME = 0x02
C_MU_ACQUIRE = 0x03
C_MU_RELEASE = 0x04
C_MU_ACQ_REL = 0x05
C_MU_SEQ_CST = 0x06
MU_NOT_ATOMIC = 0x00
MU_RELAXED = 0x01
MU_CONSUME = 0x02
MU_ACQUIRE = 0x03
MU_RELEASE = 0x04
MU_ACQ_REL = 0x05
MU_SEQ_CST = 0x06
CMuAtomicRMWOp = ctypes.c_int
C_MU_XCHG = 0x00
C_MU_ADD = 0x01
C_MU_SUB = 0x02
C_MU_AND = 0x03
C_MU_NAND = 0x04
C_MU_OR = 0x05
C_MU_XOR = 0x06
C_MU_MAX = 0x07
C_MU_MIN = 0x08
C_MU_UMAX = 0x09
C_MU_UMIN = 0x0A
MU_XCHG = 0x00
MU_ADD = 0x01
MU_SUB = 0x02
MU_AND = 0x03
MU_NAND = 0x04
MU_OR = 0x05
MU_XOR = 0x06
MU_MAX = 0x07
MU_MIN = 0x08
MU_UMAX = 0x09
MU_UMIN = 0x0A
CMuCallConv = ctypes.c_int
C_MU_DEFAULT = 0x00
MU_DEFAULT = 0x00
#### High-level types which does type checking at run time.
......@@ -163,6 +163,12 @@ class MuValue(_LowLevelTypeWrapper):
_assert_instance(high_level_struct, MuCtx)
return cls(v, high_level_struct)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.delete()
def delete(self):
self.ctx.delete_value(self)
......@@ -198,6 +204,7 @@ class _StructOfMethodsWrapper(_LowLevelTypeWrapper):
""" High-level wrapper of "struct-of-method" types. """
# _high_level_methods need to be assigned externally.
# _muvm needs to refer to the associated MuVM object.
def __init__(self, struct_ptr, parent=None):
"""
......@@ -215,19 +222,16 @@ class _StructOfMethodsWrapper(_LowLevelTypeWrapper):
@classmethod
def from_low_level_retval(cls, v, high_level_struct):
print("called.", v, high_level_struct)
return cls(v, high_level_struct)
def _high_level_method(self, name):
return self._high_level_methods[name]
def _low_level_method(self, name):
def _low_level_func(self, name):
struct_ptr = self._struct_ptr
ptr_contents = struct_ptr.contents
method = getattr(ptr_contents, name)
def wrapper(*args):
return method(struct_ptr, *args)
return wrapper
func = getattr(ptr_contents, name)
return func
def __getattr__(self, name):
high_level_method = self._high_level_method(name)
......@@ -244,11 +248,18 @@ class MuVM(_StructOfMethodsWrapper):
_c_struct_type_ = CMuVM
_ctype_ = ctypes.POINTER(_c_struct_type_)
## The following overrides the raw C functions:
def __init__(self, struct_ptr, dll):
super(self.__class__, self).__init__(struct_ptr, dll)
self.muvm = self
self._mu_error_addr = self._low_level_func("get_mu_error_ptr_")(struct_ptr)
self._mu_error = ctypes.c_int.from_address(self._mu_error_addr)
def get_mu_error(self):
return self._mu_error.value
#def new_context(self):
#ptr = self._low_level_method("new_context")()
#return MuCtx(ptr)
def set_mu_error(self, v):
self._mu_error.value = v
_MIN_SINT64 = (-1)<<63
_MAX_SINT64 = (1<<63)-1
......@@ -258,14 +269,30 @@ class MuCtx(_StructOfMethodsWrapper):
_c_struct_type_ = CMuCtx
_ctype_ = ctypes.POINTER(_c_struct_type_)
## Context management. Auto close after "with".
def __init__(self, struct_ptr, muvm):
super(self.__class__, self).__init__(struct_ptr, muvm)
self.muvm = muvm
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type != None:
return False
self.close_context()
## The following extends the C functions to make it more Pythonic
def load_bundle(self, bundle_str):
_assert_instance(bundle_str, str)
_assert_instance(bundle_str, str, unicode)
return self.load_bundle_(bundle_str, len(bundle_str))
def load_hail(self, hail_str):
_assert_instance(hail_str, str)
_assert_instance(hail_str, str, unicode)
return self.load_hail_(hail_str, len(hail_str))
def handle_from_int(self, value, length):
......@@ -284,6 +311,38 @@ class MuCtx(_StructOfMethodsWrapper):
def handle_to_uint(self, value):
return self.handle_to_uint64_(value)
def delete_values(self, *values):
for value in values:
self.delete_value(value)
def insert_element(self, value, index, newval):
return self.insert_element_(value, index, newval).cast(type(value))
def load(self, loc, ord=MU_NOT_ATOMIC):
_assert_instance(loc, MuIRefValue)
return self.load_(ord, loc)
def store(self, loc, newval, ord=MU_NOT_ATOMIC):
_assert_instance(loc, MuIRefValue)
return self.store_(ord, loc, newval)
def cmpxchg(self, loc, expected, desired, weak=False, ord_succ=MU_SEQ_CST,
ord_fail=MU_SEQ_CST):
_assert_instance(loc, MuIRefValue)
weak = int(weak)
succ_buf = ctypes.c_int(0)
rv = self.cmpxchg_(ord_succ, ord_fail, weak, loc, expected, desired,
ctypes.byref(succ_buf))
succ = succ_buf.value != 0
return (rv, succ)
def atomicrmw(self, op, loc, opnd, ord=MU_SEQ_CST):
_assert_instance(loc, MuIRefValue)
return self.atomicrmw_(ord, op, loc, opnd)
def fence(self, ord=MU_SEQ_CST):
return self.fence_(ord)
def _to_low_level_type(ty):
return (None if ty == None else
ty._ctype_ if issubclass(ty, _LowLevelTypeWrapper) else
......@@ -319,8 +378,16 @@ def _make_high_level_method(name, expected_nargs, restype, argtypes):
low_level_args.append(low_level_arg)
low_level_method = self._low_level_method(name)
low_level_rv = low_level_method(*low_level_args)
struct_ptr = self._struct_ptr
low_level_func = self._low_level_func(name)
self.muvm.set_mu_error(0)
low_level_rv = low_level_func(struct_ptr, *low_level_args)
mu_error = self.muvm.get_mu_error()
if mu_error != 0:
raise RuntimeError("Error in Mu. mu_error={}".format(mu_error))
rv = _from_low_level_retval(restype, low_level_rv, self)
return rv
......@@ -353,7 +420,7 @@ def _initialize_methods(high_level_class, methods):
# make low-level struct field (function pointer)
low_level_restype = _to_low_level_type(restype)
low_level_argtypes = [_to_low_level_type(ty) for ty in argtypes]
print(name, low_level_restype, low_level_argtypes)
print("Python binding:", name, low_level_restype, low_level_argtypes)
funcptr = _funcptr(
low_level_restype, # return value
objtype_p, *low_level_argtypes # params. Start with a struct ptr
......@@ -377,6 +444,7 @@ _initialize_methods(MuVM, [
("name_of", CMuName, [CMuID]),
("set_trap_handler_", None, [CMuTrapHandler, CMuCPtr]),
("execute", None, []),
("get_mu_error_ptr_", ctypes.c_void_p, []),
])
_initialize_methods(MuCtx, [
......@@ -388,27 +456,27 @@ _initialize_methods(MuCtx, [
("load_bundle_" , None , [ctypes.c_char_p, ctypes.c_int]),
("load_hail_" , None , [ctypes.c_char_p, ctypes.c_int]),
("handle_from_sint8_" , MuIntValue , [ctypes.c_byte, ctypes.c_int]),
("handle_from_uint8_" , MuIntValue , [ctypes.c_ubyte, ctypes.c_int]),
("handle_from_sint16_" , MuIntValue , [ctypes.c_short, ctypes.c_int]),
("handle_from_uint16_" , MuIntValue , [ctypes.c_ushort, ctypes.c_int]),
("handle_from_sint32_" , MuIntValue , [ctypes.c_int, ctypes.c_int]),
("handle_from_uint32_" , MuIntValue , [ctypes.c_uint, ctypes.c_int]),
("handle_from_sint64_" , MuIntValue , [ctypes.c_longlong, ctypes.c_int]),
("handle_from_uint64_" , MuIntValue , [ctypes.c_ulonglong, ctypes.c_int]),
("handle_from_sint8_" , MuIntValue , [ctypes.c_int8, ctypes.c_int]),
("handle_from_uint8_" , MuIntValue , [ctypes.c_uint8, ctypes.c_int]),
("handle_from_sint16_" , MuIntValue , [ctypes.c_int16, ctypes.c_int]),
("handle_from_uint16_" , MuIntValue , [ctypes.c_uint16, ctypes.c_int]),
("handle_from_sint32_" , MuIntValue , [ctypes.c_int32, ctypes.c_int]),
("handle_from_uint32_" , MuIntValue , [ctypes.c_uint32, ctypes.c_int]),
("handle_from_sint64_" , MuIntValue , [ctypes.c_int64, ctypes.c_int]),
("handle_from_uint64_" , MuIntValue , [ctypes.c_uint64, ctypes.c_int]),
("handle_from_float" , MuFloatValue , [ctypes.c_float ]),
("handle_from_double" , MuDoubleValue , [ctypes.c_double]),
("handle_from_ptr" , MuUPtrValue , [CMuID, CMuCPtr ]),
("handle_from_fp" , MuUFPValue , [CMuID, CMuCFP ]),
("handle_to_sint8_" , ctypes.c_byte , [MuIntValue]),
("handle_to_uint8_" , ctypes.c_ubyte , [MuIntValue]),
("handle_to_sint16_" , ctypes.c_short , [MuIntValue]),
("handle_to_uint16_" , ctypes.c_ushort , [MuIntValue]),
("handle_to_sint32_" , ctypes.c_int , [MuIntValue]),
("handle_to_uint32_" , ctypes.c_uint , [MuIntValue]),
("handle_to_sint64_" , ctypes.c_longlong , [MuIntValue]),
("handle_to_uint64_" , ctypes.c_ulonglong, [MuIntValue]),
("handle_to_sint8_" , ctypes.c_int8 , [MuIntValue]),
("handle_to_uint8_" , ctypes.c_uint8 , [MuIntValue]),
("handle_to_sint16_" , ctypes.c_int16 , [MuIntValue]),
("handle_to_uint16_" , ctypes.c_uint16 , [MuIntValue]),
("handle_to_sint32_" , ctypes.c_int32 , [MuIntValue]),
("handle_to_uint32_" , ctypes.c_uint32 , [MuIntValue]),
("handle_to_sint64_" , ctypes.c_int64 , [MuIntValue]),
("handle_to_uint64_" , ctypes.c_uint64 , [MuIntValue]),
("handle_to_float" , ctypes.c_float , [MuFloatValue ]),
("handle_to_double" , ctypes.c_double , [MuDoubleValue]),
("handle_to_ptr" , CMuCPtr , [MuUPtrValue ]),
......@@ -428,7 +496,7 @@ _initialize_methods(MuCtx, [
("insert_value" , MuStructValue , [MuStructValue, ctypes.c_int, MuValue]),
("extract_element" , MuValue , [MuSeqValue, MuIntValue]),
("insert_element" , MuSeqValue , [MuSeqValue, MuIntValue, MuValue]),
("insert_element_" , MuSeqValue , [MuSeqValue, MuIntValue, MuValue]),
("new_fixed" , MuRefValue , [CMuID]),
("new_hybrid" , MuRefValue , [CMuID, MuIntValue]),
......@@ -441,11 +509,13 @@ _initialize_methods(MuCtx, [
("shift_iref" , MuIRefValue , [MuIRefValue, MuIntValue]),
("get_var_part_iref" , MuIRefValue , [MuIRefValue]),
("load" , MuValue , [CMuMemOrd, MuIRefValue]),
("store" , None , [CMuMemOrd, MuIRefValue, MuValue]),
("cmpxchg" , MuValue , [CMuMemOrd, CMuMemOrd, ctypes.c_int, MuIRefValue, MuValue, MuValue, ctypes.c_int]),
("atomicrmw" , MuValue , [CMuMemOrd, CMuAtomicRMWOp, MuIRefValue, MuValue]),
("fence" , None , [CMuMemOrd]),
("load_" , MuValue , [CMuMemOrd, MuIRefValue]),
("store_" , None , [CMuMemOrd, MuIRefValue, MuValue]),
("cmpxchg_" , MuValue , [CMuMemOrd, CMuMemOrd,
ctypes.c_int, MuIRefValue, MuValue, MuValue,
ctypes.POINTER(ctypes.c_int)]),
("atomicrmw_" , MuValue , [CMuMemOrd, CMuAtomicRMWOp, MuIRefValue, MuValue]),
("fence_" , None , [CMuMemOrd]),
("new_stack" , MuStackRefValue , [MuFuncRefValue]),
("new_thread" , MuThreadRefValue , [MuStackRefValue, CMuHowToResume, MuValue, ctypes.c_int, MuRefValue]),
......@@ -485,6 +555,27 @@ _initialize_methods(MuCtx, [
("unexpose" , None , [CMuCallConv, MuValue]),
])
class DelayedDisposer(object):
def __init__(self):
self.garbages = []
def __exit__(self, exc_type, exc_val, exc_tb):
self.delete_all()
def add(self, handle):
self.garbages.append(handle)
return handle
def __lshift__(self, rhs):
return self.add(rhs)
def delete_all(self):
if exc_type != None:
return False
for h in reversed(self.garbages):
h.delete();
class MuRefImpl2StartDLL(object):
def __init__(self, dll):
if isinstance(dll, str) or isinstance(dll, unicode):
......@@ -504,6 +595,6 @@ class MuRefImpl2StartDLL(object):
def mu_refimpl2_new(self):
ptr = self.dll.mu_refimpl2_new()
return MuVM(ptr)
return MuVM(ptr, self)
# vim: ts=4 sw=4 et sts=4 ai tw=80
......@@ -2,22 +2,201 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import unittest
from libmupython2 import *
dll = MuRefImpl2StartDLL(u"libmurefimpl2start.so")
mu = dll.mu_refimpl2_new()
ctx = mu.new_context()
h = ctx.handle_from_sint64_(100, 64)
v = ctx.handle_to_sint64_(h)
print("v=",v)
assert(v==100)
h2 = ctx.handle_from_int(2147483648, 32)
v21 = ctx.handle_to_sint(h2)
print("v21=", v21)
assert(v21==-2147483648)
v22 = ctx.handle_to_uint(h2)
print("v22=", v22)
assert(v22==2147483648)
with mu.new_context() as ctx:
ctx.load_bundle("""
.typedef @i8 = int<8>
.typedef @i16 = int<16>
.typedef @i32 = int<32>
.typedef @i64 = int<64>
.typedef @float = float
.typedef @double = double
.const @FOO <@i64> = 10000
.const @BAR <@double> = 3.25d
.global @BAZ <@i64>
.funcsig @fib.sig = (@i64) -> (@i64)
.funcdecl @fib <@fib.sig>
.typedef @s1 = struct<@i32 @i64>
.typedef @a1 = array<@i32 5>
.typedef @4xi32 = vector<@i32 4>
""" +
"\n".join(".const @I32_{} <@i32> = {}".format(i,i) for i in range(20))
+ """
.const @S1 <@s1> = {@I32_1 @I32_2}
.const @A1 <@a1> = {@I32_3 @I32_4 @I32_5 @I32_6 @I32_7}
.const @V1 <@4xi32> = {@I32_11 @I32_12 @I32_13 @I32_14}
.typedef @h1 = hybrid<@i32 @i64 @i8>
""")
class TestRefImpl2CBinding(unittest.TestCase):
def setUp(self):
self.ctx = mu.new_context()
def tearDown(self):
self.ctx.close_context()
def test_basics(self):
h = self.ctx.handle_from_sint64_(100, 64)
v = self.ctx.handle_to_sint64_(h)
print("v=",v)
self.assertEqual(v, 100)
h2 = self.ctx.handle_from_int(2147483648, 32)
v21 = self.ctx.handle_to_sint(h2)
print("v21=", v21)
self.assertEqual(v21, -2147483648)
v22 = self.ctx.handle_to_uint(h2)
print("v22=", v22)
self.assertEqual(v22, 2147483648)
def test_globalvars(self):
ctx = self.ctx
id_of = ctx.id_of
h1 = ctx.handle_from_const(id_of("@FOO")).cast(MuIntValue)
h2 = ctx.handle_from_const(id_of("@BAR")).cast(MuDoubleValue)
h3 = ctx.handle_from_global(id_of("@BAZ"))
h4 = ctx.handle_from_func(id_of("@fib"))
v1 = ctx.handle_to_sint(h1)
self.assertEqual(v1, 10000)
v2 = ctx.handle_to_double(h2)
self.assertEqual(v2, 3.25)
self.assertIsInstance(h3, MuIRefValue)
self.assertIsInstance(h4, MuFuncRefValue)
ctx.delete_value(h1)
ctx.delete_value(h2)
ctx.delete_values(h3,h4)
def test_aggregate(self):
ctx = self.ctx
id_of = ctx.id_of
s1 = ctx.handle_from_const(id_of("@S1")).cast(MuStructValue)
a1 = ctx.handle_from_const(id_of("@A1")).cast(MuArrayValue)
v1 = ctx.handle_from_const(id_of("@V1")).cast(MuVectorValue)
s10 = ctx.extract_value(s1, 0).cast(MuIntValue)
s11 = ctx.extract_value(s1, 1).cast(MuIntValue)
vs10 = ctx.handle_to_sint(s10)
vs11 = ctx.handle_to_sint(s11)
self.assertEqual(vs10, 1)
self.assertEqual(vs11, 2)
for i in range(5):
hi = ctx.handle_from_int(i, 64)
a1i = ctx.extract_element(a1, hi).cast(MuIntValue)
va1i = ctx.handle_to_sint(a1i)
self.assertEqual(va1i, 3+i, "va1{} != {}".format(i, 3+i))
for i in range(4):
hi = ctx.handle_from_int(i, 64)
v1i = ctx.extract_element(v1, hi).cast(MuIntValue)
vv1i = ctx.handle_to_sint(v1i)
self.assertEqual(vv1i, 11+i, "vv1{} != {}".format(i, 3+i))
zero = ctx.handle_from_int(0, 32)
one = ctx.handle_from_int(1, 32)
s2 = ctx.insert_value(s1, 0, one)
a2 = ctx.insert_element(a1, zero, one)
v2 = ctx.insert_element(v1, zero, one)
self.assertIsInstance(s2, MuStructValue)
self.assertIsInstance(a2, MuArrayValue)
self.assertIsInstance(v2, MuVectorValue)
def test_mem(self):
ctx = self.ctx
id_of = ctx.id_of
r1 = ctx.new_fixed(id_of("@s1"))
ir1 = ctx.get_iref(r1)
ir10 = ctx.get_field_iref(ir1, 0)
ir11 = ctx.get_field_iref(ir1, 1)
hnum1 = ctx.handle_from_int(100, 32)
hnum2 = ctx.handle_from_int(200, 64)
ctx.store(ir10, hnum1, ord=MU_SEQ_CST)
hr10 = ctx.load(ir10, ord=MU_SEQ_CST).cast(MuIntValue)
vr10 = ctx.handle_to_sint(hr10)
self.assertEqual(vr10, 100)
ctx.store(ir11, hnum2)
hr11 = ctx.load(ir11).cast(MuIntValue)
vr11 = ctx.handle_to_sint(hr11)
self.assertEqual(vr11, 200)
r2 = ctx.new_fixed(id_of("@a1"))
ir2 = ctx.get_iref(r2)
zero = ctx.handle_from_int(0, 64)
three = ctx.handle_from_int(3, 64)
ir20 = ctx.get_elem_iref(ir2, zero)
ir23 = ctx.get_elem_iref(ir2, three)
ir23s = ctx.shift_iref(ir20, three)
self.assertEqual(ctx.ref_eq(ir23, ir23s), 1)
self.assertEqual(ctx.ref_eq(ir20, ir23), 0)
self.assertEqual(ctx.ref_ult(ir20, ir23), 1)
self.assertEqual(ctx.ref_ult(ir23, ir23s), 0)
ctx.store(ir23, hnum1)
hr23 = ctx.load(ir23s).cast(MuIntValue)
vr23 = ctx.handle_to_sint(hr23)
self.assertEqual(vr23, 100)
r3 = ctx.new_hybrid(id_of("@h1"), three)
ir3 = ctx.get_iref(r3)
ir3f0 = ctx.get_field_iref(ir3, 0)
ir3f1 = ctx.get_field_iref(ir3, 1)
ir3v = ctx.get_var_part_iref(ir3)
ir3v3 = ctx.shift_iref(ir3v, three)
self.assertTrue(ctx.ref_eq(ir3, ir3f0))
self.assertTrue(ctx.ref_ult(ir3f0, ir3v))
def test_atomic(self):
ctx = self.ctx
id_of = ctx.id_of
ir = ctx.handle_from_global(id_of("@BAZ"))
h100 = ctx.handle_from_int(100, 64)
h200 = ctx.handle_from_int(200, 64)
h300 = ctx.handle_from_int(300, 64)
ctx.store(ir, h100, ord=MU_SEQ_CST)
h, succ = ctx.cmpxchg(ir, h100, h200)
h = h.cast(MuIntValue)
v = ctx.handle_to_sint(h)
self.assertEqual(v, 100)
self.assertTrue(succ)
h2, succ2 = ctx.cmpxchg(ir, h100, h300)
h2 = h2.cast(MuIntValue)
v2 = ctx.handle_to_sint(h2)
self.assertEqual(v2, 200)
self.assertFalse(succ2)
h3 = ctx.atomicrmw(MU_ADD, ir, h300).cast(MuIntValue)
h4 = ctx.load(ir, ord=MU_SEQ_CST).cast(MuIntValue)
v3 = ctx.handle_to_sint(h3)
v4 = ctx.handle_to_sint(h4)
self.assertEqual(v3, 200)
self.assertEqual(v4, 500)
......@@ -129,6 +129,7 @@ struct MuVM {
// Proprietary API: let the micro VM execute
void (*execute)(MuVM *mvm);
int* (*get_mu_error_ptr)(MuVM *mvm);
};
// A local context. It can only be used by one thread at a time. It holds many
......
......@@ -165,6 +165,7 @@ object NativeMuVM {
microVM.setTrapHandler(new NativeTrapHandler(trap_handler, userdata))
}
def execute(microVM: MicroVM): Unit = microVM.execute()
def get_mu_error_ptr(microVM: MicroVM): MuCPtr = ClientAccessibleClassExposer.muErrorPtr.address()
}
/**
......@@ -506,6 +507,12 @@ object ClientAccessibleClassExposer {
case t if t =:= TMuCtx => JType.POINTER
case t if t <:< TMuValue => JType.POINTER
}
val MU_NATIVE_ERRNO = 6481626 // muErrno is set to this number if an exception is thrown
val muErrorPtr = jnrMemoryManager.allocateDirect(16)
def getMuError(): Int = muErrorPtr.getInt(0)
def setMuError(errno: Int): Unit = muErrorPtr.putInt(0, errno)
/**
* Let a native program call a reflectively-exposed method.
......@@ -519,6 +526,9 @@ object ClientAccessibleClassExposer {
} catch {
case e: Throwable => {
logger.error("Exception thrown before returning to native. This is fatal", e)
System.out.flush()
System.err.flush()
setMuError(MU_NATIVE_ERRNO)
throw e
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment