So many changes. Broken.
[invirt/third/libt4.git] / marshall.py
1 import os, numpy as np
2 from clang.cindex import *
3 import subprocess
4
5 libt4_path = '/Users/peteriannucci/invirt/third/libt4/'
6 OPTFLAGS = ['-O3']
7 STDLIB = ['-stdlib=libc++']
8 PEDANTRY = ['-Werror', '-Weverything', '-Wall', '-Wextra', '-pedantic-errors', '-pedantic',
9                     '-Wno-c++98-compat-pedantic', '-Wno-padded', '-Weffc++',
10                     '-Wno-non-virtual-dtor', '-Wno-weak-vtables']
11 CXXFLAGS = ['-x', 'c++', '-I', libt4_path, '-std=c++1y'] + STDLIB + PEDANTRY + OPTFLAGS
12 CXXFLAGS += ['-DMARSHALL_RAW_NETWORK_ORDER_AS(a,b)=static void macro_marshall_##a##_as_##b(a first, b second);']
13
14 target = 'paxos.cc'
15
16 libdir = subprocess.Popen(['llvm-config-mp-3.4', '--libdir'], stdout=subprocess.PIPE).communicate()[0].strip()
17 Config.set_library_path(libdir)
18 index = Index.create()
19 tu = index.parse(os.path.join(libt4_path, target), args=CXXFLAGS)
20
21 def dive_all(cursor, kinds, pred=None, depth_limit=-1):
22     if cursor.kind in kinds and (pred is None or pred(cursor)):
23         yield cursor
24     if depth_limit != 0:
25         for c in cursor.get_children():
26             for d in dive_all(c, kinds, pred, depth_limit-1):
27                 yield d
28
29 def get_label(cursor):
30     try:
31         cursor = dive_all(cursor, (CursorKind.CONVERSION_FUNCTION,), lambda c: c.displayname == 'operator label()', 1).next()
32         cursor = dive_all(cursor, (CursorKind.STRING_LITERAL,)).next()
33         return eval(cursor.get_tokens().next().spelling)
34     except:
35         return None
36
37 def get_members(cursor):
38     try:
39         adapter = dive_all(cursor, (CursorKind.CXX_METHOD,), lambda c: c.displayname == '_tuple_()', 1).next()
40         fields = {x.displayname:x for x in dive_all(cursor, (CursorKind.FIELD_DECL,))}
41         return [fields[m.displayname] for m in dive_all(adapter, (CursorKind.MEMBER_REF_EXPR,))]
42     except:
43         return None
44
45 class Marshallable:
46     def __init__(self, m, u):
47         self.marshall = m
48         self.unmarshall = u
49
50 def MarshallRawNetworkOrderAs(what, as_what):
51     as_what = np.dtype(as_what).newbyteorder('B')
52     def m(x):
53         return np.array([x], as_what).tostring()
54     def u(s):
55         return np.fromstring(s[:as_what.itemsize], as_what).astype(what)[0], s[as_what.itemsize:]
56     mu = Marshallable(m, u)
57     mu.__repr__ = lambda : '<Marshallable(%s)>' % np.dtype(what).name
58     return mu
59
60 def RecordClass(displayname, members):
61     member_names = [f.displayname for f in members]
62     member_types = [f.type.get_canonical().spelling for f in members]
63     member_types_nice = [f.type.spelling for f in members]
64     member_marshallers = [types[t] for t in member_types]
65     class Record(object):
66         def __init__(self, *args, **kwargs):
67             if args:
68                 assert len(args) == len(member_names)
69                 self.__dict__.update(dict(zip(member_names, args)))
70             else:
71                 assert set(kwargs.keys()) == set(member_names)
72                 self.__dict__.update(kwargs)
73         __init__.__doc__ = 'Required arguments:\n' + '\n'.join('%s (%s)' % (n,t) for t,n in zip(member_types_nice, member_names))
74         @staticmethod
75         def marshall(self):
76             return ''.join(marshaller.marshall(getattr(self, name)) for name, marshaller in zip(member_names, member_marshallers))
77         @staticmethod
78         def unmarshall(s):
79             member_values = []
80             for marshaller in member_marshallers:
81                 member_value, s = marshaller.unmarshall(s)
82                 member_values.append(member_value)
83             return Record(**dict(zip(member_names, member_values))), s
84         def __repr__(self):
85             return '<%s ' % displayname + ' '.join('%s=%s' % (k, repr(getattr(self, k))) for k in member_names) + '>'
86     Record.__name__ = displayname
87     return Record
88
89 class MarshallString:
90     def __init__(self):
91         pass
92     def marshall(self, s):
93         return types['unsigned int'].marshall(len(s)) + s
94     def unmarshall(self, s):
95         l, s = types['unsigned int'].unmarshall(s)
96         return s[:l], s[l:]
97
98 types = {}
99 labels = {}
100 types['std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >'] = MarshallString()
101
102 numpy_type = {'bool': np.bool, 'char': np.int8, 'signed char': np.int8, 'unsigned char': np.uint8, 'short': np.short, 'unsigned short': np.ushort,
103               'int': np.intc, 'unsigned int': np.uintc, 'long': np.int_, 'unsigned long': np.uint, 'long long': np.longlong, 'unsigned long long': np.ulonglong}
104
105 def processDeclaration(cursor):
106     if cursor.kind == CursorKind.ENUM_DECL:
107         enum_type = cursor.enum_type.get_canonical().spelling
108         if enum_type != '<dependent type>':
109             processDeclaration(cursor.enum_type.get_canonical().get_declaration())
110             assert enum_type in types, enum_type
111             types[cursor.type.get_canonical().spelling] = types[enum_type]
112     elif cursor.kind == CursorKind.FUNCTION_DECL:
113         if cursor.displayname.startswith('macro_marshall_'):
114             what, as_what = [x.type.get_canonical().spelling for x in cursor.get_arguments()]
115             types[what] = MarshallRawNetworkOrderAs(numpy_type[what], numpy_type[as_what])
116     else:
117         members = get_members(cursor)
118         label = get_label(cursor)
119         if members is not None:
120             for f in members:
121                 field_type = f.type.get_canonical().spelling
122                 processDeclaration(f.type.get_canonical().get_declaration())
123                 assert field_type in types, field_type
124             types[cursor.type.get_canonical().spelling] = RecordClass(cursor.displayname, members)
125         if label is not None:
126             labels[cursor.type.get_canonical().spelling] = label
127
128 for cursor in dive_all(tu.cursor, (CursorKind.FUNCTION_DECL,)):
129     processDeclaration(cursor)
130
131 for cursor in dive_all(tu.cursor, set([CursorKind.STRUCT_DECL, CursorKind.CLASS_DECL, CursorKind.ENUM_DECL])):
132     processDeclaration(cursor)