To protect your data, the CISO officer has suggested users to enable GitLab 2FA as soon as possible.

Commit 92b9a2e6 authored by Kunshan Wang's avatar Kunshan Wang
Browse files

WIP: Python binding type check

parent 5d43b544
......@@ -10,3 +10,4 @@ target
libmupython2: Python2 binding for the Mu-client API.
For PyPy users: This module is not RPython!
The Mu reference implementation 2 contains a ``cbinding`` directory which
produces the ```` library. Users of this Python binding
should construct a MuRefImpl2StartDLL object with the pathname of that shared
object as the argument to the constructor.
This binding is a "medium-level" binding. It is more Python-friendly than the
low-level raw C API, and it does a lot of run-time type checking when API
functions are invoked, but it does not provide higher-level functionalities than
the C API, such as the LLVM-style CFG builder.
from __future__ import division, absolute_import, print_function, unicode_literals
import ctypes
#### Low-level C types counterpart
def _funcptr(restype, *paramtypes, **kwargs):
return ctypes.CFUNCTYPE(restype, *paramtypes, **kwargs)
......@@ -101,88 +119,266 @@ CMuCallConv = ctypes.c_int
def _fields_of_struct_of_methods(objtype, methods):
This function constructs the _fields_ field of MuVM, MuCtx and MuBuilder
structs. All of them starts with a (void*) header and many function pointer,
all of which take a pointer of this struct as the first argument.
#### High-level types which does type checking at run time.
``objtype`` is the type of the struct (such as CMuVM, CMuCtx).
class _LowLevelTypeWrapper(object):
# _ctype_ = the low-level type
``methods`` is a list of (name, restype, paramtypes) where paramtypes do
not include the first argument, which is implicitly
def to_low_level_arg(self):
raise NotImplementedError()
fields = [("header", ctypes.c_void_p)]
def from_low_level_retval(cls, v, high_level_struct):
raise NotImplementedError()
objtype_p = ctypes.POINTER(objtype)
class MuValue(_LowLevelTypeWrapper):
_ctype_ = CMuValue
def __init__(self, c_mu_value, ctx):
self.c_mu_value = c_mu_value
self.ctx = ctx
for name, restype, paramtypes in methods:
funcptr = _funcptr(restype, objtype_p, *paramtypes)
fields.append((name, funcptr))
def to_low_level_arg(self):
return self.c_mu_value
return fields
def from_low_level_retval(cls, v, high_level_struct):
assert(isinstance(high_level_struct, MuCtx))
return cls(v, high_level_struct)
CMuVM._fields_ = _fields_of_struct_of_methods(CMuVM, [
("new_context", CPtrMuCtx, []),
("id_of", CMuID, [CMuName]),
("name_of", CMuName, [CMuID]),
("set_trap_handler", None, [CMuTrapHandler, CMuCPtr]),
("execute", None, []),
def delete(self):
CMuCtx._fields_ = _fields_of_struct_of_methods(CMuCtx, [
("id_of", CMuID, [CMuName]),
("name_of", CMuName, [CMuID]),
def __str__(self):
return "<{} handle={}>".format(type(self).__name__, self.c_mu_value)
("close_context", None, []),
def __repr__(self):
return str(self)
("load_bundle", None, [ctypes.c_char_p, ctypes.c_int]),
("load_hail", None, [ctypes.c_char_p, ctypes.c_int]),
class MuIntValue (MuValue): _ctype_ = CMuIntValue
class MuFloatValue (MuValue): _ctype_ = CMuFloatValue
class MuDoubleValue (MuValue): _ctype_ = CMuDoubleValue
class MuRefValue (MuValue): _ctype_ = CMuRefValue
class MuIRefValue (MuValue): _ctype_ = CMuIRefValue
class MuStructValue (MuValue): _ctype_ = CMuStructValue
class MuArrayValue (MuValue): _ctype_ = CMuArrayValue
class MuVectorValue (MuValue): _ctype_ = CMuVectorValue
class MuFuncRefValue (MuValue): _ctype_ = CMuFuncRefValue
class MuThreadRefValue (MuValue): _ctype_ = CMuThreadRefValue
class MuStackRefValue (MuValue): _ctype_ = CMuStackRefValue
class MuFCRefValue (MuValue): _ctype_ = CMuFCRefValue
class MuTagRef64Value (MuValue): _ctype_ = CMuTagRef64Value
class MuUPtrValue (MuValue): _ctype_ = CMuUPtrValue
class MuUFPValue (MuValue): _ctype_ = CMuUFPValue
("handle_from_sint8", CMuIntValue, [ctypes.c_byte, ctypes.c_int]),
("handle_from_uint8", CMuIntValue, [ctypes.c_ubyte, ctypes.c_int]),
("handle_from_sint16", CMuIntValue, [ctypes.c_short, ctypes.c_int]),
("handle_from_uint16", CMuIntValue, [ctypes.c_ushort, ctypes.c_int]),
("handle_from_sint32", CMuIntValue, [ctypes.c_int, ctypes.c_int]),
("handle_from_uint32", CMuIntValue, [ctypes.c_uint, ctypes.c_int]),
("handle_from_sint64", CMuIntValue, [ctypes.c_longlong, ctypes.c_int]),
("handle_from_uint64", CMuIntValue, [ctypes.c_ulonglong, ctypes.c_int]),
class _StructOfMethodsWrapper(_LowLevelTypeWrapper):
""" High-level wrapper of "struct-of-method" types. """
# _high_level_methods need to be assigned externally.
class _StructOfMethodsWrapper(object):
def __init__(self, _struct_ptr):
self._struct_ptr = _struct_ptr
print("_struct_ptr:", _struct_ptr)
def __init__(self, struct_ptr, parent=None):
struct_ptr: pointer to the underlying C struct
parent: the Python object that returns this object. For example, If a
MuVM returns a MuCtx via its ``new_context`` method, then the MuVM is
the parent of the MuCtx.
self._struct_ptr = struct_ptr
self._parent = parent
def to_low_level_arg(self):
return self._struct_ptr
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):
struct_ptr = self._struct_ptr
method = getattr(self._struct_ptr.contents, name)
ptr_contents = struct_ptr.contents
method = getattr(ptr_contents, name)
def wrapper(*args):
return method(struct_ptr, *args)
return wrapper
def __getattr__(self, name):
return self._low_level_method(name)
high_level_method = self._high_level_method(name)
return lambda *a: high_level_method(self, *a)
def __str__(self):
return "<{} _struct_ptr={}>".format(type(self).__name__,
def __repr__(self):
return str(self)
class MuVM(_StructOfMethodsWrapper):
_struct_type_ = CMuVM
_c_struct_type_ = CMuVM
_ctype_ = ctypes.POINTER(_c_struct_type_)
## The following overrides the raw C functions:
def new_context(self):
ptr = self._low_level_method("new_context")()
return MuCtx(ptr)
#def new_context(self):
#ptr = self._low_level_method("new_context")()
#return MuCtx(ptr)
class MuCtx(_StructOfMethodsWrapper):
_struct_type_ = CMuCtx
_c_struct_type_ = CMuCtx
_ctype_ = ctypes.POINTER(_c_struct_type_)
## The following extends the C functions to make it more Pythonic
def load_bundle_from_str(self, bundle_str):
assert(isinstance(bundle_str, str))
return self.load_bundle(bundle_str, len(bundle_str))
def load_hail_from_str(self, hail_str):
assert(isinstance(hail_str, str))
return self.load_hail(hail_str, len(hail_str))
def _to_low_level_type(ty):
return (None if ty == None else
ty._ctype_ if issubclass(ty, _LowLevelTypeWrapper) else
def _to_low_level_arg(arg):
return (arg.to_low_level_arg() if isinstance(arg, _LowLevelTypeWrapper)
else arg)
def _from_low_level_retval(restype, low_level_rv, self):
return (restype.from_low_level_retval(low_level_rv, self)
if issubclass(restype, _LowLevelTypeWrapper)
else low_level_rv)
def _make_high_level_method(name, expected_nargs, restype, argtypes):
def wrapper(self, *args):
nargs = len(args)
if nargs != expected_nargs:
raise TypeError("{}() takes {} positional argument but "
"{} were given".format(name, expected_nargs, nargs))
low_level_args = []
for i, arg in enumerate(args):
argtype = argtypes[i]
if issubclass(argtype, _LowLevelTypeWrapper):
# type checking
if not isinstance(arg, argtype):
raise TypeError("Method {}, arg {}, expected type {}, "
"actual type: {}, value: {}".format(name, i,
argtypes[i], type(arg), arg))
low_level_arg = _to_low_level_arg(arg)
low_level_method = self._low_level_method(name)
low_level_rv = low_level_method(*low_level_args)
rv = _from_low_level_retval(restype, low_level_rv, self)
return rv
return wrapper
def _initialize_methods(high_level_class, methods):
This function does two things:
1. Populate the high_level_class (such as MuVM) with high-level methods.
2. Populate the _field_ field of its low-level Structure (such as CMuVM).
``high_level_class`` is MuVM or MuCtx.
``method`` is a list of (name, restype, argtypes), where both restype and
the elements of argtypes are high-level types, such as MuVM or MuIntValue.
Type checking will be performed according to these descriptions in the
generated high-level methods. C-level return values will be wrapped in
high-level types.
low_level_class = high_level_class._c_struct_type_
fields = [("header", ctypes.c_void_p)]
high_level_methods = {}
objtype_p = ctypes.POINTER(low_level_class)
for name, restype, argtypes in 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)
funcptr = _funcptr(
low_level_restype, # return value
objtype_p, *low_level_argtypes # params. Start with a struct ptr
fields.append((name, funcptr))
expected_nargs = len(argtypes)
def load_bundle_from_str(self, bundle):
return self.load_bundle(bundle, len(bundle))
# make high-level method
wrapper = _make_high_level_method(name, expected_nargs, restype,
def load_hail_from_str(self, hail):
return self.load_hail(hail, len(hail))
high_level_methods[name] = wrapper
low_level_class._fields_ = fields
high_level_class._high_level_methods = high_level_methods
_initialize_methods(MuVM, [
("new_context", MuCtx, []),
("id_of", CMuID, [CMuName]),
("name_of", CMuName, [CMuID]),
("set_trap_handler", None, [CMuTrapHandler, CMuCPtr]),
("execute", None, []),
_initialize_methods(MuCtx, [
("id_of", CMuID, [CMuName]),
("name_of", CMuName, [CMuID]),
("close_context", None, []),
("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_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_float" , ctypes.c_float , [MuFloatValue ]),
("handle_to_double", ctypes.c_double , [MuDoubleValue]),
("handle_to_ptr" , CMuCPtr , [MuUPtrValue ]),
("handle_to_fp" , CMuCFP , [MuUFPValue ]),
("handle_from_const" , MuValue , [CMuID]),
("handle_from_global", MuIRefValue , [CMuID]),
("handle_from_func" , MuFuncRefValue, [CMuID]),
("handle_from_expose", MuValue , [CMuID]),
("delete_value", None, [MuValue]),
class MuRefImpl2StartDLL(object):
def __init__(self, dll):
......@@ -204,3 +400,5 @@ class MuRefImpl2StartDLL(object):
def mu_refimpl2_new(self):
ptr = self.dll.mu_refimpl2_new()
return MuVM(ptr)
# vim: ts=4 sw=4 et sts=4 ai tw=80
#!/usr/bin/env python2
from __future__ import division, absolute_import, print_function, unicode_literals
from libmupython2 import *
dll = MuRefImpl2StartDLL(u"")
mu = dll.mu_refimpl2_new()
ctx = mu.new_context()
h = ctx.handle_from_sint64(100, 64)
v = ctx.handle_to_sint64(h)
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