-
Notifications
You must be signed in to change notification settings - Fork 34
/
wat2wasm4tests.py
executable file
·158 lines (122 loc) · 4.63 KB
/
wat2wasm4tests.py
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
#!/usr/bin/python3
"""wat2wasm4tests
This script converts C++ or Rust comments containing WebAssembly text format (WAT)
into WebAssembly binary format.
It uses wat2wasm tool from WABT (https://github.com/WebAssembly/wabt)
and expect the tool be available in PATH.
- On Linux/Debian wabt package is available.
- On macOS wabt can be installed via homebrew.
It searches for the C++ or Rust block comments starting with `wat2wasm [options]`.
Optional options are going to be passed to the wat2wasm tool.
Example:
/* wat2wasm --no-check
(module
(func ...)
)
*/
"""
import os
import re
import subprocess
import sys
DEBUG = False
WAT2WASM_TOOL = 'wat2wasm'
WAT2WASM_DEFAULT_OPTIONS = ['--disable-saturating-float-to-int',
'--disable-sign-extension', '--disable-multi-value']
CPP_FORMAT_TOOL = ['clang-format', '-i']
RS_FORMAT_TOOL = ['rustfmt']
CPP_EXTENSION = ".cpp"
RS_EXTENSION = ".rs"
WAT_RE = re.compile(r'/\* wat2wasm(.*)\n([^*]*)\*/', re.MULTILINE)
WASM_CPP_RE = re.compile(r'\s*(?:const )?auto \w+ =\s*(?:fizzy\:\:test\:\:)?from_hex\(\s*"([^;]*)"\);',
re.MULTILINE)
WASM_RUST_RE = re.compile(r'\s*let \w+ =\s*hex::decode\(\s*"([^;]*)"\)\s*.unwrap\(\);',
re.MULTILINE)
TMP_WAT_FILE = sys.argv[0] + '.wat'
TMP_WASM_FILE = sys.argv[0] + '.wasm'
def debug(*args, **kwargs):
if DEBUG:
print(*args, **kwargs)
def report_wat_errors(errmsg, source, source_path, wat_pos):
wat_line = source.count('\n', 0, wat_pos)
line_re = re.compile(TMP_WAT_FILE.replace('.', '\\.') + r':(\d+):')
p = 0
while True:
line_match = line_re.search(errmsg, p)
if not line_match:
break
n = int(line_match.group(1))
s = line_match.span(1)
errmsg = "".join((errmsg[:s[0]], str(wat_line + n), errmsg[s[1]:]))
p = line_match.span()[1]
errmsg = errmsg.replace(TMP_WAT_FILE, source_path)
print(errmsg, file=sys.stderr)
try:
if len(sys.argv) < 2:
raise Exception("Missing FILE argument")
source_path = sys.argv[1]
source_extension = os.path.splitext(source_path)[1]
if source_extension == CPP_EXTENSION:
cpp_source = True
elif source_extension == RS_EXTENSION:
cpp_source = False
else:
raise Exception("File extension not supported")
with open(source_path, 'r') as f:
source = f.read()
modified = False
pos = 0
while True:
wat_match = WAT_RE.search(source, pos)
if not wat_match:
break
wat = wat_match.group(2)
options = wat_match.group(1)
options = options.split() if options else []
pos = wat_match.span()[1]
debug(f"wat2wasm: {options}\n{wat}")
with open(TMP_WAT_FILE, 'w') as f:
f.write(wat)
options = WAT2WASM_DEFAULT_OPTIONS + options
r = subprocess.run([WAT2WASM_TOOL, TMP_WAT_FILE] + options,
capture_output=True, text=True)
if r.returncode != 0:
report_wat_errors(r.stderr, source, source_path,
wat_match.span(2)[0])
continue
with open(TMP_WASM_FILE, 'rb') as f:
new_wasm = f.read().hex()
wasm_re = WASM_CPP_RE if cpp_source else WASM_RUST_RE
wasm_match = wasm_re.match(source, pos)
if wasm_match:
cur_wasm = wasm_match.group(1)
cur_wasm = cur_wasm.translate({ord(c): '' for c in ' \r\n"'})
if new_wasm != cur_wasm:
modified = True
begin, end = wasm_match.span(1)
source = "".join((source[:begin], new_wasm, source[end:]))
else:
modified = True
if cpp_source:
source = "".join((source[:pos], 'const auto wasm = from_hex("',
new_wasm, '");', source[pos:]))
else:
source = "".join((source[:pos], 'let wasm = hex::decode("',
new_wasm, '").unwrap();', source[pos:]))
if modified:
with open(source_path, 'w') as f:
f.write(source)
# Format the modified file with clang-format / rustfmt, but ignore all the related
# errors, including formatting tool not found.
format_tool = CPP_FORMAT_TOOL if cpp_source else RS_FORMAT_TOOL
try:
subprocess.run(format_tool + [source_path],
capture_output=True)
except:
pass
except Exception as ex:
print(f"{ex}", file=sys.stderr)
if os.path.exists(TMP_WAT_FILE):
os.remove(TMP_WAT_FILE)
if os.path.exists(TMP_WASM_FILE):
os.remove(TMP_WASM_FILE)