1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
# Copyright 2020 The Emscripten Authors. All rights reserved.
# Emscripten is available under two separate licenses, the MIT license and the
# University of Illinois/NCSA Open Source License. Both these licenses can be
# found in the LICENSE file.
import os
import re
from tools.shared import Settings, path_from_root, unsuffixed, config, check_call, exit_with_error
# map an emscripten-style signature letter to a wasm2c C type
def s_to_c(s):
if s == 'v':
return 'void'
elif s == 'i':
return 'u32'
elif s == 'j':
return 'u64'
elif s == 'f':
return 'f32'
elif s == 'd':
return 'f64'
else:
exit_with_error('invalid sig element:' + str(s))
# map a wasm2c C type to an emscripten-style signature letter
def c_to_s(c):
if c == 'WASM_RT_I32':
return 'i'
elif c == 'WASM_RT_I64':
return 'j'
elif c == 'WASM_RT_F32':
return 'f'
elif c == 'WASM_RT_F64':
return 'd'
else:
exit_with_error('invalid wasm2c type element:' + str(c))
def get_func_types(code):
'''
We look for this pattern:
static void init_func_types(void) {
func_types[0] = wasm_rt_register_func_type(3, 1, WASM_RT_I32, WASM_RT_I32, WASM_RT_I32, WASM_RT_I32);
func_types[1] = wasm_rt_register_func_type(1, 1, WASM_RT_I32, WASM_RT_I32);
func_types[2] = wasm_rt_register_func_type(0, 0);
}
We return a map of signatures names to their index.
'''
init_func_types = re.search(r'static void init_func_types\(void\) {([^}]*)}', code)
if not init_func_types:
return {}
ret = {}
for entry in re.findall(r'func_types\[(\d+)\] = wasm_rt_register_func_type\((\d+), (\d+),? ?([^)]+)?\);', init_func_types[0]):
index, params, results, types = entry
index = int(index)
params = int(params)
results = int(results)
types = types.split(', ')
sig = ''
for i in range(params):
sig += c_to_s(types[i])
if results == 0:
sig = 'v' + sig
else:
assert results == 1, 'no multivalue support'
sig = c_to_s(types[-1]) + sig
ret[sig] = index
return ret
def do_wasm2c(infile):
assert Settings.STANDALONE_WASM
WASM2C = config.NODE_JS + [path_from_root('node_modules', 'wasm2c', 'wasm2c.js')]
WASM2C_DIR = path_from_root('node_modules', 'wasm2c')
c_file = unsuffixed(infile) + '.wasm.c'
h_file = unsuffixed(infile) + '.wasm.h'
cmd = WASM2C + [infile, '-o', c_file]
check_call(cmd)
total = '''\
/*
* This file was generated by emcc+wasm2c. To compile it, use something like
*
* $CC FILE.c -O2 -lm -DWASM_RT_MAX_CALL_STACK_DEPTH=8000
*/
'''
SEP = '\n/* ==================================== */\n'
def bundle_file(total, filename):
with open(filename) as f:
total += '// ' + filename + '\n' + f.read() + SEP
return total
# hermeticize the C file, by bundling in the wasm2c/ includes
headers = [
(WASM2C_DIR, 'wasm-rt.h'),
(WASM2C_DIR, 'wasm-rt-impl.h'),
('', h_file)
]
for header in headers:
total = bundle_file(total, os.path.join(header[0], header[1]))
# add the wasm2c output
with open(c_file) as read_c:
c = read_c.read()
total += c + SEP
# add the wasm2c runtime
total = bundle_file(total, os.path.join(WASM2C_DIR, 'wasm-rt-impl.c'))
# add the support code
support_files = ['base']
if Settings.AUTODEBUG:
support_files.append('autodebug')
if Settings.EXPECT_MAIN:
# TODO: add an option for direct OS access. For now, do that when building
# an executable with main, as opposed to a library
support_files.append('os')
support_files.append('main')
else:
support_files.append('os_sandboxed')
support_files.append('reactor')
# for a reactor, also append wasmbox_* API definitions
with open(h_file, 'a') as f:
f.write('''
// wasmbox_* API
// TODO: optional prefixing
extern void wasmbox_init(void);
''')
for support_file in support_files:
total = bundle_file(total, path_from_root('tools', 'wasm2c', support_file + '.c'))
# remove #includes of the headers we bundled
for header in headers:
total = total.replace('#include "%s"\n' % header[1], '/* include of %s */\n' % header[1])
# generate the necessary invokes
invokes = []
for sig in re.findall(r"\/\* import\: 'env' 'invoke_(\w+)' \*\/", total):
all_func_types = get_func_types(total)
def name(i):
return 'a' + str(i)
wabt_sig = sig[0] + 'i' + sig[1:]
typed_args = [s_to_c(sig[i]) + ' ' + name(i) for i in range(1, len(sig))]
full_typed_args = ['u32 fptr'] + typed_args
types = [s_to_c(sig[i]) for i in range(1, len(sig))]
args = [name(i) for i in range(1, len(sig))]
c_func_type = s_to_c(sig[0]) + ' (*)(' + (', '.join(types) if types else 'void') + ')'
if sig not in all_func_types:
exit_with_error('could not find signature ' + sig + ' in function types ' + str(all_func_types))
type_index = all_func_types[sig]
invokes.append(r'''
IMPORT_IMPL(%(return_type)s, Z_envZ_invoke_%(sig)sZ_%(wabt_sig)s, (%(full_typed_args)s), {
VERBOSE_LOG("invoke\n"); // waka
u32 sp = Z_stackSaveZ_iv();
if (next_setjmp >= MAX_SETJMP_STACK) {
abort_with_message("too many nested setjmps");
}
u32 id = next_setjmp++;
int result = setjmp(setjmp_stack[id]);
%(declare_return)s
if (result == 0) {
%(receive)sCALL_INDIRECT(w2c___indirect_function_table, %(c_func_type)s, %(type_index)s, fptr %(args)s);
/* if we got here, no longjmp or exception happened, we returned normally */
} else {
/* A longjmp or an exception took us here. */
Z_stackRestoreZ_vi(sp);
Z_setThrewZ_vii(1, 0);
}
next_setjmp--;
%(return)s
});
''' % {
'return_type': s_to_c(sig[0]) if sig[0] != 'v' else 'void',
'sig': sig,
'wabt_sig': wabt_sig,
'full_typed_args': ', '.join(full_typed_args),
'type_index': type_index,
'c_func_type': c_func_type,
'args': (', ' + ', '.join(args)) if args else '',
'declare_return': (s_to_c(sig[0]) + ' returned_value = 0;') if sig[0] != 'v' else '',
'receive': 'returned_value = ' if sig[0] != 'v' else '',
'return': 'return returned_value;' if sig[0] != 'v' else ''
})
total += '\n'.join(invokes)
# write out the final file
with open(c_file, 'w') as out:
out.write(total)
|