2023-04-12 14:21:03 +00:00
#!/usr/bin/env python3
2023-07-29 04:46:56 +00:00
from argparse import ArgumentParser
import json
def needs_arg_ptr_stub ( decl ) :
res = ( decl [ ' ret ' ] [ ' tag ' ] == ' S ' )
for arg in decl [ ' args ' ] :
if arg [ ' type ' ] [ ' tag ' ] == ' S ' :
res = True
return ( res )
2023-09-07 05:55:14 +00:00
def printError ( str ) :
# This prints a string with a red foreground color.
# See this link for an explanation of console escape codes: https://stackabuse.com/how-to-print-colored-text-in-python/
print ( " \x1b [38;5;196m " + " error: " + str + " \033 [0;0m " )
2023-07-29 04:46:56 +00:00
2023-08-08 09:38:43 +00:00
def bindgen ( apiName , spec , * * kwargs ) :
guest_stubs_path = kwargs . get ( " guest_stubs " )
guest_include = kwargs . get ( " guest_include " )
wasm3_bindings_path = kwargs . get ( " wasm3_bindings " )
2023-07-29 04:46:56 +00:00
2023-08-08 09:38:43 +00:00
if guest_stubs_path == None :
guest_stubs_path = ' bindgen_ ' + apiName + ' _guest_stubs.c '
if wasm3_bindings_path == None :
wasm3_bindings_path = ' bindgen_ ' + apiName + ' _wasm3_bindings.c '
2023-07-29 04:46:56 +00:00
2023-08-08 09:38:43 +00:00
host_bindings = open ( wasm3_bindings_path , ' w ' )
guest_bindings = None
2023-07-29 04:46:56 +00:00
2023-08-08 09:38:43 +00:00
specFile = open ( spec , ' r ' )
data = json . load ( specFile )
2023-07-29 04:46:56 +00:00
2023-08-08 09:38:43 +00:00
for decl in data :
if needs_arg_ptr_stub ( decl ) :
guest_bindings = open ( guest_stubs_path , ' w ' )
if guest_include != None :
s = ' #include " ' + guest_include + ' " \n \n '
print ( s , file = guest_bindings )
break
2023-07-29 04:46:56 +00:00
2023-08-08 09:38:43 +00:00
for decl in data :
2023-04-12 14:21:03 +00:00
2023-08-08 09:38:43 +00:00
name = decl [ ' name ' ]
cname = decl . get ( ' cname ' , name )
2023-04-12 14:21:03 +00:00
2023-08-08 09:38:43 +00:00
if needs_arg_ptr_stub ( decl ) :
argPtrStubName = name + ' _argptr_stub '
# pointer arg stub declaration
s = ' '
if decl [ ' ret ' ] [ ' tag ' ] == ' S ' :
s + = ' void '
else :
s + = decl [ ' ret ' ] [ ' name ' ]
s + = ' ORCA_IMPORT( ' + argPtrStubName + ' ) ( '
if decl [ ' ret ' ] [ ' tag ' ] == ' S ' :
s + = decl [ ' ret ' ] [ ' name ' ] + ' * __retArg '
if len ( decl [ ' args ' ] ) > 0 :
s + = ' , '
for i , arg in enumerate ( decl [ ' args ' ] ) :
s + = arg [ ' type ' ] [ ' name ' ]
if arg [ ' type ' ] [ ' tag ' ] == ' S ' :
s + = ' * '
s + = ' ' + arg [ ' name ' ]
if i + 1 < len ( decl [ ' args ' ] ) :
s + = ' , '
s + = ' ); \n \n '
# forward function to pointer arg stub declaration
s + = decl [ ' ret ' ] [ ' name ' ] + ' ' + name + ' ( '
for i , arg in enumerate ( decl [ ' args ' ] ) :
s + = arg [ ' type ' ] [ ' name ' ] + ' ' + arg [ ' name ' ]
if i + 1 < len ( decl [ ' args ' ] ) :
s + = ' , '
s + = ' ) \n '
s + = ' { \n '
s + = ' \t '
if decl [ ' ret ' ] [ ' tag ' ] == ' S ' :
s + = decl [ ' ret ' ] [ ' name ' ] + ' __ret; \n \t '
elif decl [ ' ret ' ] [ ' tag ' ] != ' v ' :
s + = decl [ ' ret ' ] [ ' name ' ]
s + = ' __ret = '
s + = argPtrStubName + ' ( '
if decl [ ' ret ' ] [ ' tag ' ] == ' S ' :
s + = ' &__ret '
if len ( decl [ ' args ' ] ) > 0 :
s + = ' , '
for i , arg in enumerate ( decl [ ' args ' ] ) :
if arg [ ' type ' ] [ ' tag ' ] == ' S ' :
s + = ' & '
s + = arg [ ' name ' ]
if i + 1 < len ( decl [ ' args ' ] ) :
s + = ' , '
s + = ' ); \n '
if decl [ ' ret ' ] [ ' tag ' ] != ' v ' :
s + = ' \t return(__ret); \n '
s + = ' } \n \n '
2023-04-12 14:21:03 +00:00
2023-08-08 09:38:43 +00:00
print ( s , file = guest_bindings )
2023-07-29 04:46:56 +00:00
2023-08-08 09:38:43 +00:00
# host-side stub
s = ' const void* ' + cname + ' _stub(IM3Runtime runtime, IM3ImportContext _ctx, uint64_t* _sp, void* _mem) '
2023-04-12 14:21:03 +00:00
2023-08-08 09:38:43 +00:00
gen_stub = decl . get ( ' gen_stub ' , True )
if gen_stub == False :
s + = ' ; \n \n '
else :
2023-08-03 09:37:32 +00:00
s + = ' \n { \n '
2023-08-08 09:38:43 +00:00
2023-08-03 09:37:32 +00:00
# NOTE: check and cast arguments
retTag = decl [ ' ret ' ] [ ' tag ' ]
2023-08-08 09:38:43 +00:00
firstArgIndex = 0
if retTag != ' v ' :
firstArgIndex = 1
2023-09-12 13:03:20 +00:00
if retTag == ' S ' :
retTypeName = decl [ ' ret ' ] [ ' name ' ]
retTypeCName = decl [ ' ret ' ] . get ( ' cname ' , retTypeName )
s + = retTypeCName + ' * __retPtr = ( ' + retTypeCName + ' *)((char*)_mem + *(i32*)&_sp[0]); \n '
2023-08-03 09:37:32 +00:00
for argIndex , arg in enumerate ( decl [ ' args ' ] ) :
argName = arg [ ' name ' ]
2023-08-08 09:38:43 +00:00
typeName = arg [ ' type ' ] [ ' name ' ]
typeCName = arg [ ' type ' ] . get ( ' cname ' , typeName )
argTag = arg [ ' type ' ] [ ' tag ' ]
2023-08-03 09:37:32 +00:00
s + = ' \t '
2023-08-08 09:38:43 +00:00
if argTag == ' i ' :
2023-08-03 09:37:32 +00:00
s + = typeCName + ' ' + argName + ' = ( ' + typeCName + ' )*(i32*)&_sp[ ' + str ( firstArgIndex + argIndex ) + ' ]; \n '
2023-08-08 09:38:43 +00:00
elif argTag == ' I ' :
2023-08-03 09:37:32 +00:00
s + = typeCName + ' ' + argName + ' = ( ' + typeCName + ' )*(i64*)&_sp[ ' + str ( firstArgIndex + argIndex ) + ' ]; \n '
2023-08-08 09:38:43 +00:00
elif argTag == ' f ' :
2023-08-03 09:37:32 +00:00
s + = typeCName + ' ' + argName + ' = ( ' + typeCName + ' )*(f32*)&_sp[ ' + str ( firstArgIndex + argIndex ) + ' ]; \n '
2023-08-08 09:38:43 +00:00
elif argTag == ' F ' :
2023-08-03 09:37:32 +00:00
s + = typeCName + ' ' + argName + ' = ( ' + typeCName + ' )*(f64*)&_sp[ ' + str ( firstArgIndex + argIndex ) + ' ]; \n '
2023-08-08 09:38:43 +00:00
elif argTag == ' p ' :
2023-08-03 09:37:32 +00:00
s + = typeCName + ' ' + argName + ' = ( ' + typeCName + ' )((char*)_mem + *(u32*)&_sp[ ' + str ( firstArgIndex + argIndex ) + ' ]); \n '
2023-08-08 09:38:43 +00:00
elif argTag == ' S ' :
2023-08-03 09:37:32 +00:00
s + = typeCName + ' ' + argName + ' = *( ' + typeCName + ' *)((char*)_mem + *(u32*)&_sp[ ' + str ( firstArgIndex + argIndex ) + ' ]); \n '
2023-08-08 09:38:43 +00:00
else :
print ( ' unrecognized type ' + c + ' in procedure signature \n ' )
break
2023-08-03 09:37:32 +00:00
# check pointer arg length
for arg in decl [ ' args ' ] :
argName = arg [ ' name ' ]
typeName = arg [ ' type ' ] [ ' name ' ]
typeCName = arg [ ' type ' ] . get ( ' cname ' , typeName )
argTag = arg [ ' type ' ] [ ' tag ' ]
argLen = arg . get ( ' len ' )
2023-09-07 05:55:14 +00:00
if argTag == ' p ' :
if argLen == None :
printError ( " binding ' " + name + " ' missing pointer length decoration for param ' " + argName + " ' " )
2023-08-03 09:37:32 +00:00
else :
2023-09-07 05:55:14 +00:00
s + = ' \t { \n '
s + = ' \t \t OC_ASSERT(((char*) ' + argName + ' >= (char*)_mem) && (((char*) ' + argName + ' - (char*)_mem) < m3_GetMemorySize(runtime)), " parameter \' ' + argName + ' \' is out of bounds " ); \n '
s + = ' \t \t OC_ASSERT((char*) ' + argName + ' + '
proc = argLen . get ( ' proc ' )
if proc != None :
s + = proc + ' (runtime, '
lenProcArgs = argLen [ ' args ' ]
for i , lenProcArg in enumerate ( lenProcArgs ) :
s + = lenProcArg
if i < len ( lenProcArgs ) - 1 :
s + = ' , '
s + = ' ) '
else :
components = argLen . get ( ' components ' )
countArg = argLen . get ( ' count ' )
if components != None :
s + = str ( components )
if countArg != None :
s + = ' * '
2023-08-03 09:37:32 +00:00
if countArg != None :
2023-09-07 05:55:14 +00:00
s + = countArg
2023-08-03 09:37:32 +00:00
2023-09-07 05:55:14 +00:00
if typeCName . endswith ( ' ** ' ) or ( typeCName . startswith ( ' void ' ) == False and typeCName . startswith ( ' const void ' ) == False ) :
s + = ' *sizeof( ' + typeCName [ : - 1 ] + ' ) '
2023-08-03 09:37:32 +00:00
2023-09-07 05:55:14 +00:00
s + = ' <= ((char*)_mem + m3_GetMemorySize(runtime)), " parameter \' ' + argName + ' \' overflows wasm memory " ); \n '
s + = ' \t } \n '
2023-08-03 09:37:32 +00:00
s + = ' \t '
if retTag == ' i ' :
s + = ' *((i32*)&_sp[0]) = (i32) '
elif retTag == ' I ' :
s + = ' *((i64*)&_sp[0]) = (i64) '
elif retTag == ' f ' :
s + = ' *((f32*)&_sp[0]) = (f32) '
elif retTag == ' F ' :
s + = ' *((f64*)&_sp[0]) = (f64) '
elif retTag == ' S ' :
2023-09-12 13:03:20 +00:00
s + = ' *__retPtr = '
2023-08-03 09:37:32 +00:00
elif retTag == ' p ' :
print ( " Warning: " + name + " : pointer return type not supported yet " )
s + = cname + ' ( '
for i , arg in enumerate ( decl [ ' args ' ] ) :
s + = arg [ ' name ' ]
2023-08-08 09:38:43 +00:00
if i + 1 < len ( decl [ ' args ' ] ) :
s + = ' , '
s + = ' ); \n \t return(0); \n } \n \n '
print ( s , file = host_bindings )
# link function
2023-08-03 09:37:32 +00:00
s = ' int bindgen_link_ ' + apiName + ' _api(IM3Module module) \n { \n '
s + = ' M3Result res; \n '
s + = ' int ret = 0; \n '
2023-08-08 09:38:43 +00:00
for decl in data :
name = decl [ ' name ' ]
cname = decl . get ( ' cname ' , name )
if needs_arg_ptr_stub ( decl ) :
name = name + ' _argptr_stub '
m3Sig = ' '
if decl [ ' ret ' ] [ ' tag ' ] == ' S ' :
m3Sig + = ' v '
else :
m3Sig + = decl [ ' ret ' ] [ ' tag ' ]
2023-04-12 14:21:03 +00:00
2023-08-08 09:38:43 +00:00
m3Sig + = ' ( '
if decl [ ' ret ' ] [ ' tag ' ] == ' S ' :
m3Sig + = ' i '
for arg in decl [ ' args ' ] :
tag = arg [ ' type ' ] [ ' tag ' ]
if tag == ' p ' or tag == ' S ' :
tag = ' i '
m3Sig + = tag
m3Sig + = ' ) '
2023-04-14 09:48:36 +00:00
2023-04-12 14:21:03 +00:00
2023-08-03 09:37:32 +00:00
s + = ' res = m3_LinkRawFunction(module, " * " , " ' + name + ' " , " ' + m3Sig + ' " , ' + cname + ' _stub); \n '
s + = ' if(res != m3Err_none && res != m3Err_functionLookupFailed) \n '
s + = ' { \n '
2023-08-14 08:26:11 +00:00
s + = ' oc_log_error( " Couldn \' t link function ' + name + ' ( %s ) \\ n " , res); \n '
2023-08-03 09:37:32 +00:00
s + = ' ret = -1; \n '
s + = ' } \n \n '
2023-04-12 14:21:03 +00:00
2023-08-03 09:37:32 +00:00
s + = ' \t return(ret); \n } \n '
2023-04-12 14:21:03 +00:00
2023-08-08 09:38:43 +00:00
print ( s , file = host_bindings )
2023-04-12 14:21:03 +00:00
2023-08-08 09:38:43 +00:00
if __name__ == " __main__ " :
parser = ArgumentParser ( prog = ' bindgen.py ' )
parser . add_argument ( ' api ' )
parser . add_argument ( ' spec ' )
parser . add_argument ( ' -g ' , ' --guest-stubs ' )
parser . add_argument ( ' --guest-include ' )
parser . add_argument ( ' --wasm3-bindings ' )
args = parser . parse_args ( )
apiName = args . api
spec = args . spec
guest_stubs_path = args . guest_stubs
if guest_stubs_path == None :
guest_stubs_path = ' bindgen_ ' + apiName + ' _guest_stubs.c '
wasm3_bindings_path = args . wasm3_bindings
if wasm3_bindings_path == None :
wasm3_bindings_path = ' bindgen_ ' + apiName + ' _wasm3_bindings.c '
2023-08-03 09:37:32 +00:00
2023-08-08 09:38:43 +00:00
bindgen ( apiName , spec ,
guest_stubs = guest_stubs_path ,
guest_include = args . guest_include ,
wasm3_bindings = wasm3_bindings_path ,
)