mirror of
https://github.com/reswitched/Mephisto.git
synced 2024-06-04 22:28:50 -04:00
c2ee5f8a05
This permite to provide more up to date data. Please note that this cannot work with actual SwiPC master because it's missing a __init__.py file.
448 lines
14 KiB
Python
448 lines
14 KiB
Python
import glob, hashlib, json, os, os.path, re, sys
|
|
from pprint import pprint
|
|
from SwIPC import idparser
|
|
import partialparser
|
|
from cStringIO import StringIO
|
|
|
|
def emitInt(x):
|
|
return '0x%x' % x if x > 9 else str(x)
|
|
|
|
handlemap = dict(
|
|
session='IPipe',
|
|
port='NPort',
|
|
)
|
|
|
|
typemap = dict(
|
|
i8='int8_t',
|
|
i16='int16_t',
|
|
i32='int32_t',
|
|
i64='int64_t',
|
|
i128='int128_t',
|
|
u8='uint8_t',
|
|
u16='uint16_t',
|
|
u32='uint32_t',
|
|
u64='uint64_t',
|
|
u128='uint128_t',
|
|
f32='float32_t',
|
|
pid='gpid',
|
|
bool='bool',
|
|
)
|
|
|
|
typesizes = dict(
|
|
i8=1,
|
|
i16=2,
|
|
i32=4,
|
|
i64=8,
|
|
i128=16,
|
|
u8=1,
|
|
u16=2,
|
|
u32=4,
|
|
u64=8,
|
|
u128=16,
|
|
f32=4,
|
|
pid=8,
|
|
bool=1,
|
|
)
|
|
|
|
allTypes = None
|
|
|
|
def typeSize(type):
|
|
if type[0] in ('unknown', 'i8', 'u8'):
|
|
return 1
|
|
elif type[0] == 'bytes':
|
|
return type[1]
|
|
elif type[0] in allTypes:
|
|
return typeSize(allTypes[type[0]])
|
|
elif type[0] in typesizes:
|
|
return typesizes[type[0]]
|
|
return 1
|
|
|
|
def splitByNs(obj):
|
|
ons = {}
|
|
for type, x in obj.items():
|
|
ns = type.rsplit('::', 1)[0] if '::' in type else None
|
|
name = type.rsplit('::', 1)[-1]
|
|
if ns not in ons:
|
|
ons[ns] = {}
|
|
ons[ns][name] = x
|
|
return ons
|
|
|
|
def retype(spec, noIndex=False):
|
|
if spec[0] == 'unknown':
|
|
return 'uint8_t'
|
|
elif spec[0] == 'bytes':
|
|
return 'uint8_t%s' % ('[%s]' % emitInt(spec[1]) if not noIndex else ' *')
|
|
else:
|
|
return typemap[spec[0]] if spec[0] in typemap else spec[0];
|
|
|
|
def formatParam(param, input, i):
|
|
name, spec = param
|
|
if name is None:
|
|
name = '_%i' % i
|
|
|
|
hasSize = False
|
|
|
|
if spec[0] == 'align':
|
|
return formatParam((name, spec[2]), input, i)
|
|
|
|
if spec[0] == 'bytes':
|
|
type = 'uint8_t *'
|
|
elif spec[0] == 'unknown':
|
|
assert False
|
|
elif spec[0] == 'buffer':
|
|
type = '%s *' % retype(spec[1])
|
|
hasSize = True
|
|
elif spec[0] == 'array':
|
|
type = retype(spec[1]) + ' *'
|
|
hasSize = True
|
|
elif spec[0] == 'object':
|
|
type = 'shared_ptr<%s>' % spec[1][0]
|
|
elif spec[0] == 'KObject':
|
|
if len(spec) >= 2 and spec[2][0] in handlemap:
|
|
type = 'shared_ptr<%s>' % handlemap[spec[2][0]]
|
|
else:
|
|
type = 'shared_ptr<KObject>'
|
|
else:
|
|
type = typemap[spec[0]] if spec[0] in typemap else spec[0]
|
|
|
|
if type.endswith(']'):
|
|
arrspec = type[type.index('['):]
|
|
type = type[:-len(arrspec)]
|
|
else:
|
|
arrspec = ''
|
|
|
|
return '%s %s%s %s%s%s' % ('IN' if input else 'OUT', type, '&' if not input and (not type.endswith('*') and not arrspec) else '', name, arrspec, ', guint %s_size' % name if hasSize else '')
|
|
|
|
def generatePrototype(func):
|
|
return ', '.join([formatParam(x, True, i) for i, x in enumerate(func['inputs'])] + [formatParam(x, False, i + len(func['inputs'])) for i, x in enumerate(func['outputs'])])
|
|
|
|
def isPointerType(type):
|
|
if type[0] in typesizes:
|
|
return False
|
|
elif type[0] == 'bytes':
|
|
return True
|
|
elif type[0] in allTypes:
|
|
return isPointerType(allTypes[type[0]])
|
|
elif type[0] == 'struct':
|
|
return False
|
|
return True
|
|
|
|
INIT = 'INIT'
|
|
AFTER = 'AFTER'
|
|
|
|
def generateCaller(qname, fname, func):
|
|
def tempname():
|
|
tempI[0] += 1
|
|
return 'temp%i' % tempI[0]
|
|
params = []
|
|
logFmt, logElems = [], []
|
|
tempI = [0]
|
|
inpOffset = 8
|
|
bufOffs = {}
|
|
hndOff = 0
|
|
objOff = 0
|
|
bufSizes = 0
|
|
for name, elem in func['inputs']:
|
|
type, rest = elem[0], elem[1:]
|
|
if type in ('array', 'buffer'):
|
|
if rest[1] not in bufOffs:
|
|
bufOffs[rest[1]] = 0
|
|
cbo = bufOffs[rest[1]]
|
|
bufOffs[rest[1]] += 1
|
|
an, sn, bn = tempname(), tempname(), tempname()
|
|
yield 'guint %s;' % sn
|
|
yield 'auto %s = req.getBuffer(%s, %i, %s);' % (an, emitInt(rest[1]), cbo, sn)
|
|
yield 'auto %s = new uint8_t[%s];' % (bn, sn)
|
|
yield 'ctu->cpu.readmem(%s, %s, %s);' % (an, bn, sn)
|
|
params.append('(%s *) %s' % (retype(rest[0]), bn))
|
|
params.append(sn)
|
|
logFmt.append('%s *%s= buffer<0x" ADDRFMT ">' % (retype(rest[0]), '%s ' % name if name else ''))
|
|
logElems.append(sn)
|
|
bufSizes += 1
|
|
yield AFTER, 'delete[] %s;' % bn
|
|
elif type == 'object':
|
|
params.append('ctu->getHandle<%s>(req.getMoved(%i))' % (rest[0][0], objOff))
|
|
logFmt.append('%s %s= 0x%%x' % (rest[0][0], '%s ' % name if name else ''))
|
|
logElems.append('req.getMoved(%i)' % objOff)
|
|
objOff += 1
|
|
elif type == 'KObject':
|
|
handle_operation = rest[0][0] if len(rest) > 0 else 'copy'
|
|
req_function = 'getMoved' if handle_operation == 'move' else 'getCopied'
|
|
params.append('ctu->getHandle<KObject>(req.%s(%i))' % (req_function, hndOff))
|
|
logFmt.append('KObject %s= 0x%%x' % ('%s ' % name if name else ''))
|
|
logElems.append('req.%s(%i)' % (req_function, hndOff))
|
|
hndOff += 1
|
|
elif type == 'pid':
|
|
params.append('req.pid')
|
|
else:
|
|
if elem[0] == 'align':
|
|
alignment = elem[1]
|
|
elem = elem[2]
|
|
else:
|
|
alignment = min(8, typeSize(elem))
|
|
while inpOffset % alignment:
|
|
inpOffset += 1
|
|
if isPointerType(elem):
|
|
params.append('req.getDataPointer<%s>(%s)' % (retype(elem, noIndex=True), emitInt(inpOffset)))
|
|
logFmt.append('%s %s= %%s' % (retype(elem), '%s ' % name if name else ''))
|
|
logElems.append('bufferToString(req.getDataPointer<uint8_t *>(%s), %s).c_str()' % (emitInt(inpOffset), emitInt(typeSize(elem))))
|
|
else:
|
|
params.append('req.getData<%s>(%s)' % (retype(elem), emitInt(inpOffset)))
|
|
if typeSize(elem) == 16 or elem[0] not in typesizes:
|
|
logFmt.append('%s %s= %%s' % (retype(elem), '%s ' % name if name else ''))
|
|
logElems.append('bufferToString(req.getDataPointer<uint8_t *>(%s), %s).c_str()' % (emitInt(inpOffset), emitInt(typeSize(elem))))
|
|
else:
|
|
type = retype(elem)
|
|
ct = '0x%x'
|
|
if type == 'float32_t':
|
|
ct = '%f'
|
|
elif typeSize(elem) == 8:
|
|
ct = '0x" ADDRFMT "'
|
|
logFmt.append('%s %s= %s' % (type, '%s ' % name if name else '', ct))
|
|
logElems.append('%sreq.getData<%s>(%s)' % ('(double) ' if type == 'float32_t' else '', type, emitInt(inpOffset)))
|
|
inpOffset += typeSize(elem)
|
|
|
|
outOffset = 8
|
|
hndOff = 0
|
|
objOff = 0
|
|
for _, elem in func['outputs']:
|
|
type, rest = elem[0], elem[1:]
|
|
if type in ('array', 'buffer'):
|
|
if rest[1] not in bufOffs:
|
|
bufOffs[rest[1]] = 0
|
|
cbo = bufOffs[rest[1]]
|
|
bufOffs[rest[1]] += 1
|
|
an, sn, bn = tempname(), tempname(), tempname()
|
|
yield 'guint %s;' % sn
|
|
yield 'auto %s = req.getBuffer(%s, %i, %s);' % (an, emitInt(rest[1]), cbo, sn)
|
|
yield 'auto %s = new uint8_t[%s];' % (bn, sn)
|
|
params.append('(%s *) %s' % (retype(rest[0]), bn))
|
|
params.append(sn)
|
|
bufSizes += 1
|
|
yield AFTER, 'ctu->cpu.writemem(%s, %s, %s);' % (an, bn, sn)
|
|
yield AFTER, 'delete[] %s;' % bn
|
|
elif type == 'object':
|
|
tn = tempname()
|
|
yield 'shared_ptr<%s> %s;' % (rest[0][0], tn)
|
|
params.append(tn)
|
|
yield AFTER, 'if(%s != nullptr)' % tn
|
|
yield AFTER, '\tresp.move(%i, createHandle(%s));' % (objOff, tn)
|
|
objOff += 1
|
|
elif type == 'KObject':
|
|
handle_operation = rest[0][0] if len(rest) > 0 else 'copy'
|
|
resp_function = 'move' if handle_operation == 'move' else 'copy'
|
|
handle_creation_fnction = 'createHandle' if handle_operation == 'move' else 'ctu->newHandle'
|
|
tn = tempname()
|
|
if len(rest) > 1 and rest[1][0] in handlemap:
|
|
definition = 'shared_ptr<%s>' % handlemap[rest[1][0]]
|
|
else:
|
|
definition = 'shared_ptr<KObject>'
|
|
yield '%s %s;' % (definition, tn)
|
|
params.append(tn)
|
|
yield AFTER, 'if(%s != nullptr)' % tn
|
|
yield AFTER, '\tresp.%s(%i, %s(%s));' % (resp_function, hndOff, handle_creation_fnction, tn)
|
|
if resp_function == 'move':
|
|
objOff += 1
|
|
else:
|
|
hndOff += 1
|
|
elif type == 'pid':
|
|
assert False
|
|
else:
|
|
if elem[0] == 'align':
|
|
alignment = elem[1]
|
|
elem = elem[2]
|
|
else:
|
|
alignment = min(8, typeSize(elem))
|
|
while outOffset % alignment:
|
|
outOffset += 1
|
|
if isPointerType(elem):
|
|
tn = tempname()
|
|
yield 'auto %s = resp.getDataPointer<%s>(%s);' % (tn, retype(elem, noIndex=True), emitInt(outOffset))
|
|
params.append(tn)
|
|
else:
|
|
params.append('*resp.getDataPointer<%s *>(%s)' % (retype(elem), emitInt(outOffset)))
|
|
outOffset += typeSize(elem)
|
|
|
|
if len(func['outputs']) + len(func['inputs']) + bufSizes != len(params):
|
|
yield 'return 0xf601;'
|
|
return
|
|
|
|
yield INIT, 'resp.initialize(%i, %i, %i);' % (objOff, hndOff, outOffset - 8)
|
|
if len(logFmt):
|
|
yield 'LOG_DEBUG(IpcStubs, "IPC message to %s: %s"%s);' % (qname + '::' + fname, ', '.join(logFmt), (', ' + ', '.join(logElems)) if logElems else '')
|
|
else:
|
|
yield 'LOG_DEBUG(IpcStubs, "IPC message to %s");' % (qname + '::' + fname)
|
|
yield 'resp.errCode = %s(%s);' % (fname, ', '.join(params))
|
|
yield AFTER
|
|
yield 'return 0;'
|
|
|
|
def reorder(gen):
|
|
after = []
|
|
before = []
|
|
for x in gen:
|
|
if x == AFTER:
|
|
for elem in after:
|
|
yield elem
|
|
elif isinstance(x, tuple) and x[0] == INIT:
|
|
yield x[1]
|
|
for elem in before:
|
|
yield elem
|
|
before = None
|
|
elif isinstance(x, tuple) and x[0] == AFTER:
|
|
after.append(x[1])
|
|
elif before is not None:
|
|
before.append(x)
|
|
else:
|
|
yield x
|
|
|
|
def parsePartials(code):
|
|
code = '\n'.join(re.findall(r'/\*\$IPC\$(.*?)\*/', code, re.M|re.S))
|
|
return partialparser.parse(code)
|
|
|
|
usedInts = []
|
|
def uniqInt(*args):
|
|
args = ''.join(map(str, args))
|
|
i = int(hashlib.md5(args).hexdigest()[:8], 16)
|
|
while True:
|
|
if i not in usedInts:
|
|
usedInts.append(i)
|
|
return i
|
|
i += 1
|
|
|
|
def main():
|
|
global allTypes
|
|
|
|
fns = ['SwIPC/ipcdefs/auto.id'] + [x for x in glob.glob('SwIPC/ipcdefs/*.id') if x != 'SwIPC/ipcdefs/auto.id']
|
|
|
|
if os.path.exists('SwIPC/ipcdefs/cache') and all(os.path.getmtime('SwIPC/ipcdefs/cache') > os.path.getmtime(x) for x in fns):
|
|
res = json.load(file('SwIPC/ipcdefs/cache'))
|
|
else:
|
|
res = idparser.parse('\n'.join(file(fn).read() for fn in fns))
|
|
with file('SwIPC/ipcdefs/cache', 'w') as fp:
|
|
json.dump(res, fp)
|
|
types, ifaces, services = res
|
|
|
|
allTypes = types
|
|
|
|
typesByNs = splitByNs(types)
|
|
ifacesByNs = splitByNs(ifaces)
|
|
|
|
namespaces = {x : [] for x in typesByNs.keys() + ifacesByNs.keys()}
|
|
|
|
for ns, types in typesByNs.items():
|
|
for name, spec in sorted(types.items(), key=lambda x: x[0]):
|
|
if spec[0] == 'struct':
|
|
namespaces[ns].append('using %s = struct {' % (name))
|
|
for sub_spec in spec[1:][0]:
|
|
namespaces[ns].append('\t%s %s;' % (retype(sub_spec[1]), sub_spec[0]))
|
|
namespaces[ns].append('};')
|
|
else:
|
|
retyped, plain = retype(spec, noIndex=True), retype(spec)
|
|
namespaces[ns].append('using %s = %s;%s' % (name, retyped, ' // ' + plain if retyped != plain else ''))
|
|
|
|
for ns, ifaces in ifacesByNs.items():
|
|
for name in sorted(ifaces.keys()):
|
|
namespaces[ns].append('class %s;' % name)
|
|
|
|
fp = StringIO()
|
|
print >>fp, '#pragma once'
|
|
print >>fp, '#include "Ctu.h"'
|
|
print >>fp
|
|
|
|
print >>fp, '#define SERVICE_MAPPING() do { \\'
|
|
for iname, snames in sorted(services.items(), key=lambda x: x[0]):
|
|
for sname in snames:
|
|
print >>fp, '\tSERVICE("%s", %s); \\' % (sname, iname)
|
|
print >>fp, '} while(0)'
|
|
print >>fp
|
|
|
|
for ns, elems in sorted(namespaces.items(), key=lambda x: x[0]):
|
|
if ns is not None:
|
|
print >>fp, 'namespace %s {' % ns
|
|
hasUsing = False
|
|
for elem in elems:
|
|
if not hasUsing and elem.startswith('using'):
|
|
hasUsing = True
|
|
elif hasUsing and elem.startswith('class'):
|
|
print >>fp
|
|
hasUsing = False
|
|
print >>fp, ('\t' if ns is not None else '') + elem
|
|
if ns is not None:
|
|
print >>fp, '}'
|
|
|
|
print >>fp
|
|
|
|
allcode = '\n'.join(file(fn, 'r').read() for fn in glob.glob('ipcimpl/*.cpp'))
|
|
|
|
partials = parsePartials(allcode)
|
|
|
|
for ns, ifaces in sorted(ifacesByNs.items(), key=lambda x: x[0]):
|
|
print >>fp, '%snamespace %s {' % ('//// ' if ns is None else '', ns)
|
|
for name, funcs in sorted(ifaces.items(), key=lambda x: x[0]):
|
|
qname = '%s::%s' % (ns, name) if ns else name
|
|
partial = partials[qname] if qname in partials else None
|
|
print >>fp, '\tclass %s : public IpcService {' % name
|
|
print >>fp, '\tpublic:'
|
|
if re.search('(^|[^a-zA-Z0-9:])%s::%s[^a-zA-Z0-9:]' % (qname, name), allcode):
|
|
print >>fp, '\t\t%s(Ctu *_ctu%s);' % (name, ', ' + ', '.join('%s _%s' % (k, v) for k, v in partial[1]) if partial and partial[1] else '')
|
|
else:
|
|
print >>fp, '\t\t%s(Ctu *_ctu%s) : IpcService(_ctu)%s {}' % (name, ', ' + ', '.join('%s _%s' % (k, v) for k, v in partial[1]) if partial and partial[1] else '', ', ' + ', '.join('%s(_%s)' % (v, v) for k, v in partial[1]) if partial and partial[1] else '')
|
|
if re.search('(^|[^a-zA-Z0-9:])%s::~%s[^a-zA-Z0-9:]' % (qname, name), allcode):
|
|
print >>fp, '\t\t~%s();' % name
|
|
print >>fp, '\t\tuint32_t dispatch(IncomingIpcMessage &req, OutgoingIpcMessage &resp) {'
|
|
print >>fp, '\t\t\tswitch(req.cmdId) {'
|
|
for func in sorted(funcs['cmds'], key=lambda x: x['cmdId']):
|
|
print >>fp, '\t\t\tcase %i: {' % func['cmdId']
|
|
print >>fp, '\n'.join('\t\t\t\t' + x for x in reorder(generateCaller(qname, func['name'], func)))
|
|
print >>fp, '\t\t\t}'
|
|
print >>fp, '\t\t\tdefault:'
|
|
print >>fp, '\t\t\t\tLOG_ERROR(IpcStubs, "Unknown message cmdId %%u to interface %s", req.cmdId);' % ('%s::%s' % (ns, name) if ns else name)
|
|
print >>fp, '\t\t\t}'
|
|
print >>fp, '\t\t}'
|
|
for func in sorted(funcs['cmds'], key=lambda x: x['name']):
|
|
fname = func['name']
|
|
implemented = re.search('[^a-zA-Z0-9:]%s::%s[^a-zA-Z0-9:]' % (qname, fname), allcode)
|
|
print >>fp, '\t\tuint32_t %s(%s);' % (fname, generatePrototype(func))
|
|
if partial:
|
|
for x in partial[0]:
|
|
print >>fp, '\t\t%s' % x
|
|
print >>fp, '\t};'
|
|
print >>fp, '%s}' % ('//// ' if ns is None else '')
|
|
|
|
print >>fp, '#ifdef DEFINE_STUBS'
|
|
for name, funcs in sorted(ifaces.items(), key=lambda x: x[0]):
|
|
qname = '%s::%s' % (ns, name) if ns else name
|
|
partial = partials[qname] if qname in partials else None
|
|
for func in sorted(funcs['cmds'], key=lambda x: x['name']):
|
|
fname = func['name']
|
|
implemented = re.search('[^a-zA-Z0-9:]%s::%s[^a-zA-Z0-9:]' % (qname, fname), allcode)
|
|
if not implemented:
|
|
print >>fp, 'uint32_t %s::%s(%s) {' % (qname, fname, generatePrototype(func))
|
|
print >>fp, '\tLOG_DEBUG(IpcStubs, "Stub implementation for %s::%s");' % (qname, fname)
|
|
for i, (name, elem) in enumerate(func['outputs']):
|
|
if elem[0] == 'object' and elem[1][0] != 'IUnknown':
|
|
name = name if name else '_%i' % (len(func['inputs']) + i)
|
|
print >>fp, '\t%s = buildInterface(%s);' % (name, elem[1][0])
|
|
if elem[1][0] in partials and partials[elem[1][0]][1]:
|
|
print 'Bare construction of interface %s requiring parameters. Created in %s::%s for parameter %s' % (elem[1][0], qname, fname, name)
|
|
sys.exit(1)
|
|
elif elem[0] == 'KObject':
|
|
name = name if name else '_%i' % (len(func['inputs']) + i)
|
|
print >>fp, '\t%s = make_shared<FauxHandle>(0x%x);' % (name, uniqInt(qname, fname, name))
|
|
print >>fp, '\treturn 0;'
|
|
print >>fp, '}'
|
|
print >>fp, '#endif // DEFINE_STUBS'
|
|
|
|
code = fp.getvalue()
|
|
if os.path.exists('IpcStubs.h'):
|
|
with file('IpcStubs.h', 'r') as fp:
|
|
match = fp.read() == code
|
|
else:
|
|
match = False
|
|
if not match:
|
|
with file('IpcStubs.h', 'w') as fp:
|
|
fp.write(code)
|
|
|
|
if __name__=='__main__':
|
|
main(*sys.argv[1:])
|