nsemu/GenIpcStubs.py
2018-08-01 14:00:25 +09:00

425 lines
13 KiB
Python

import glob, hashlib, json, os, os.path, re, sys
from pprint import pprint
import Idparser, Partialparser
from cStringIO import StringIO
def emitInt(x):
return '0x%x' % x if x > 9 else str(x)
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='float',
pid='uint64_t',
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 = '%s*' % spec[1][0]
elif spec[0] == 'IpcService':
type = 'IpcService*'
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' % (type, '&' if not input and not arrspec else '', name, arrspec, ', unsigned int %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]])
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 'unsigned int %s;' % sn
yield 'auto %s = req->GetBuffer(%s, %i, %s);' % (an, emitInt(rest[1]), cbo, sn)
yield '%s* %s = (%s *) new uint8_t[%s];' % (retype(rest[0]), bn, retype(rest[0]), sn)
yield 'ARMv8::ReadBytes(%s, (uint8_t *)%s, %s);' % (an, bn, sn)
params.append('%s' % (bn))
params.append(sn)
logFmt.append('%s *%s= buffer<0x%%lx>' % (retype(rest[0]), '%s ' % name if name else ''))
logElems.append(sn)
bufSizes += 1
yield AFTER, 'delete[] (uint8_t *) %s;' % bn
elif type == 'object':
params.append('IPC::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 == 'IpcService':
params.append('IPC::GetHandle<IpcService*>(req->GetCopied(%i))' % (objOff))
logFmt.append('KObject %s= 0x%%x' % ('%s ' % name if name else ''))
logElems.append('req->GetCopied(%i)' % 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('read_string(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:
logFmt.append('%s %s= %%s' % (retype(elem), '%s ' % name if name else ''))
logElems.append('read_string(req->GetDataPointer<uint8_t *>(%s), %s).c_str()' % (emitInt(inpOffset), emitInt(typeSize(elem))))
else:
type = retype(elem)
ct = '0x%x'
if type == 'float':
ct = '%f'
elif typeSize(elem) == 8:
ct = '0x%%lx'
logFmt.append('%s %s= %s' % (type, '%s ' % name if name else '', ct))
logElems.append('%sreq->GetData<%s>(%s)' % ('(double) ' if type == 'float' 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 'unsigned int %s;' % sn
yield 'auto %s = req->GetBuffer(%s, %i, %s);' % (an, emitInt(rest[1]), cbo, sn)
yield '%s* %s = (%s *) new uint8_t[%s];' % (retype(rest[0]), bn, retype(rest[0]), sn)
params.append('%s' % (bn))
params.append(sn)
bufSizes += 1
yield AFTER, 'ARMv8::WriteBytes(%s, (uint8_t *) %s, %s);' % (an, bn, sn)
yield AFTER, 'delete[] (uint8_t *)%s;' % bn
elif type == 'object':
tn = tempname()
yield '%s* %s;' % (rest[0][0], tn)
params.append(tn)
yield AFTER, 'if(%s != nullptr)' % tn
yield AFTER, '\tresp->SetMove(%i, NewHandle((IpcService *)%s));' % (objOff, tn)
objOff += 1
elif type == 'IpcService':
tn = tempname()
yield 'IpcService *%s;' % tn
params.append(tn)
yield AFTER, 'if(%s != nullptr)' % tn
yield AFTER, '\tresp->SetCopy(%i, NewHandle(%s));' % (hndOff, tn)
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->GenBuf(%i, %i, %i);' % (objOff, hndOff, outOffset - 8)
if len(logFmt):
yield 'ns_print("IPC message to %s: %s\\n"%s);' % (qname + '::' + fname, ', '.join(logFmt), (', ' + ', '.join(logElems)) if logElems else '')
else:
yield 'ns_print("IPC message to %s\\n");' % (qname + '::' + fname)
yield 'resp->error_code = %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 = ['Ipcdefs/Auto.def'] + [x for x in glob.glob('Ipcdefs/*.def') if x != 'Ipcdefs/Auto.def']
if os.path.exists('Ipcdefs/cache') and all(os.path.getmtime('Ipcdefs/cache') > os.path.getmtime(x) for x in fns):
res = json.load(file('Ipcdefs/cache'))
else:
res = Idparser.parse('\n'.join(file(fn).read() for fn in fns))
with file('Ipcdefs/cache', 'w') as fp:
json.dump(res, fp)
types, ifaces, services = res
allTypes = types
typesByNs = splitByNs(types)
ifacesByNs = splitByNs(ifaces)
#print typesByNs, ifacesByNs
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]):
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, '#ifndef __IPCSTUBS_HPP__'
print >>fp, '#define __IPCSTUBS_HPP__'
print >>fp
# impled = (u"nn::fssrv::sf::IFileSystemProxy", u"nn::socket::sf::IClient",u"nn::socket::resolver::IResolver", u"nn::am::service::IApplicationProxyService")
# XXX:
# tmp_srvs = {k: services[k] for k in impled if k in services}
tmp_srvs = services
print tmp_srvs
print >>fp, '#define SERVICE_MAPPING() do { \\'
for iname, snames in sorted(tmp_srvs.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('Service/*.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();' % name
else:
print >>fp, '\t\t%s() {}' % name
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(IpcMessage *req, IpcMessage *resp) {'
print >>fp, '\t\t\tswitch(req->cmd_id) {'
for fname, func in sorted(funcs.items(), key=lambda x: x[1]['cmdId']):
print >>fp, '\t\t\tcase %i: {' % func['cmdId'];
print >>fp, '\n'.join('\t\t\t\t' + x for x in reorder(generateCaller(qname, fname, func)))
print >>fp, '\t\t\t}'
print >>fp, '\t\t\tdefault:'
print >>fp, '\t\t\t\tns_abort("Unknown message cmdId %%u to interface %s", req->cmd_id);' % ('%s::%s' % (ns, name) if ns else name)
print >>fp, '\t\t\t}'
print >>fp, '\t\t}'
for fname, func in sorted(funcs.items(), key=lambda x: x[0]):
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 fname, func in sorted(funcs.items(), key=lambda x: x[0]):
implemented = re.search('[^a-zA-Z0-9:]%s::%s[^a-zA-Z0-9:]' % (qname, fname), allcode)
#implemented = re.search('%s[^a-zA-Z0-9:]' % (fname), allcode)
if not implemented:
print >>fp, 'uint32_t %s::%s(%s) {' % (qname, fname, generatePrototype(func))
print >>fp, '\tns_print("Stub implementation for %s::%s\\n");' % (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'
print >>fp, '#endif // __IPCSTUBS_HPP__'
code = fp.getvalue()
if os.path.exists('include/IpcStubs.hpp'):
with file('include/IpcStubs.hpp', 'r') as fp:
match = fp.read() == code
else:
match = False
if not match:
with file('include/IpcStubs.hpp', 'w') as fp:
fp.write(code)
if __name__=='__main__':
main(*sys.argv[1:])