runtest.py 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343
  1. #!/usr/bin/env python
  2. from __future__ import print_function
  3. import argparse
  4. import array
  5. import atexit
  6. import fcntl
  7. import math
  8. import os
  9. # Pseudo-TTY and terminal manipulation
  10. import pty
  11. import re
  12. import shutil
  13. import struct
  14. import subprocess
  15. import sys
  16. import tempfile
  17. import termios
  18. import time
  19. import traceback
  20. from select import select
  21. from subprocess import PIPE, STDOUT, Popen
  22. if sys.version_info[0] == 2:
  23. IS_PY_3 = False
  24. else:
  25. IS_PY_3 = True
  26. test_aot = False
  27. # "x86_64", "i386", "aarch64", "armv7", "thumbv7", "riscv32_ilp32", "riscv32_ilp32d", "riscv32_lp64", "riscv64_lp64d"
  28. test_target = "x86_64"
  29. debug_file = None
  30. log_file = None
  31. # to save the register module with self-define name
  32. temp_file_repo = []
  33. # to save the mapping of module files in /tmp by name
  34. temp_module_table = {}
  35. def debug(data):
  36. if debug_file:
  37. debug_file.write(data)
  38. debug_file.flush()
  39. def log(data, end='\n'):
  40. if log_file:
  41. log_file.write(data + end)
  42. log_file.flush()
  43. print(data, end=end)
  44. sys.stdout.flush()
  45. # TODO: do we need to support '\n' too
  46. import platform
  47. if platform.system().find("CYGWIN_NT") >= 0:
  48. # TODO: this is weird, is this really right on Cygwin?
  49. sep = "\n\r\n"
  50. else:
  51. sep = "\r\n"
  52. rundir = None
  53. class Runner():
  54. def __init__(self, args, no_pty=False):
  55. self.no_pty = no_pty
  56. # Cleanup child process on exit
  57. atexit.register(self.cleanup)
  58. self.process = None
  59. env = os.environ
  60. env['TERM'] = 'dumb'
  61. env['INPUTRC'] = '/dev/null'
  62. env['PERL_RL'] = 'false'
  63. if no_pty:
  64. self.process = Popen(args, bufsize=0,
  65. stdin=PIPE, stdout=PIPE, stderr=STDOUT,
  66. preexec_fn=os.setsid,
  67. env=env)
  68. self.stdin = self.process.stdin
  69. self.stdout = self.process.stdout
  70. else:
  71. # Use tty to setup an interactive environment
  72. master, slave = pty.openpty()
  73. # Set terminal size large so that readline will not send
  74. # ANSI/VT escape codes when the lines are long.
  75. buf = array.array('h', [100, 200, 0, 0])
  76. fcntl.ioctl(master, termios.TIOCSWINSZ, buf, True)
  77. self.process = Popen(args, bufsize=0,
  78. stdin=slave, stdout=slave, stderr=STDOUT,
  79. preexec_fn=os.setsid,
  80. env=env)
  81. # Now close slave so that we will get an exception from
  82. # read when the child exits early
  83. # http://stackoverflow.com/questions/11165521
  84. os.close(slave)
  85. self.stdin = os.fdopen(master, 'r+b', 0)
  86. self.stdout = self.stdin
  87. self.buf = ""
  88. def read_to_prompt(self, prompts, timeout):
  89. wait_until = time.time() + timeout
  90. while time.time() < wait_until:
  91. [outs,_,_] = select([self.stdout], [], [], 1)
  92. if self.stdout in outs:
  93. read_byte = self.stdout.read(1)
  94. if not read_byte:
  95. # EOF on macOS ends up here.
  96. break
  97. read_byte = read_byte.decode('utf-8') if IS_PY_3 else read_byte
  98. debug(read_byte)
  99. if self.no_pty:
  100. self.buf += read_byte.replace('\n', '\r\n')
  101. else:
  102. self.buf += read_byte
  103. self.buf = self.buf.replace('\r\r', '\r')
  104. # filter the prompts
  105. for prompt in prompts:
  106. pattern = re.compile(prompt)
  107. match = pattern.search(self.buf)
  108. if match:
  109. end = match.end()
  110. buf = self.buf[0:end-len(prompt)]
  111. self.buf = self.buf[end:]
  112. return buf
  113. return None
  114. def writeline(self, str):
  115. str_to_write = str + '\n'
  116. str_to_write = bytes(
  117. str_to_write, 'utf-8') if IS_PY_3 else str_to_write
  118. self.stdin.write(str_to_write)
  119. def cleanup(self):
  120. if self.process:
  121. try:
  122. self.writeline("__exit__")
  123. time.sleep(.020)
  124. self.process.kill()
  125. except OSError:
  126. pass
  127. except IOError:
  128. pass
  129. self.process = None
  130. self.stdin.close()
  131. if self.stdin != self.stdout:
  132. self.stdout.close()
  133. self.stdin = None
  134. self.stdout = None
  135. if not IS_PY_3:
  136. sys.exc_clear()
  137. def assert_prompt(runner, prompts, timeout, is_need_execute_result):
  138. # Wait for the initial prompt
  139. header = runner.read_to_prompt(prompts, timeout=timeout)
  140. if not header and is_need_execute_result:
  141. log(" ---------- will terminate cause the case needs result while there is none inside of buf. ----------")
  142. sys.exit(1)
  143. if not header == None:
  144. if header:
  145. log("Started with:\n%s" % header)
  146. else:
  147. log("Did not one of following prompt(s): %s" % repr(prompts))
  148. log(" Got : %s" % repr(r.buf))
  149. sys.exit(1)
  150. ### WebAssembly specific
  151. parser = argparse.ArgumentParser(
  152. description="Run a test file against a WebAssembly interpreter")
  153. parser.add_argument('--wast2wasm', type=str,
  154. default=os.environ.get("WAST2WASM", "wast2wasm"),
  155. help="Path to wast2wasm program")
  156. parser.add_argument('--interpreter', type=str,
  157. default=os.environ.get("IWASM_CMD", "iwasm"),
  158. help="Path to WebAssembly interpreter")
  159. parser.add_argument('--aot-compiler', type=str,
  160. default=os.environ.get("WAMRC_CMD", "wamrc"),
  161. help="Path to WebAssembly AoT compiler")
  162. parser.add_argument('--no_cleanup', action='store_true',
  163. help="Keep temporary *.wasm files")
  164. parser.add_argument('--rundir',
  165. help="change to the directory before running tests")
  166. parser.add_argument('--start-timeout', default=30, type=int,
  167. help="default timeout for initial prompt")
  168. parser.add_argument('--test-timeout', default=20, type=int,
  169. help="default timeout for each individual test action")
  170. parser.add_argument('--no-pty', action='store_true',
  171. help="Use direct pipes instead of pseudo-tty")
  172. parser.add_argument('--log-file', type=str,
  173. help="Write messages to the named file in addition the screen")
  174. parser.add_argument('--log-dir', type=str,
  175. help="The log directory to save the case file if test failed")
  176. parser.add_argument('--debug-file', type=str,
  177. help="Write all test interaction the named file")
  178. parser.add_argument('test_file', type=argparse.FileType('r'),
  179. help="a WebAssembly *.wast test file")
  180. parser.add_argument('--aot', action='store_true',
  181. help="Test with AOT")
  182. parser.add_argument('--target', type=str,
  183. default="x86_64",
  184. help="Set running target")
  185. parser.add_argument('--sgx', action='store_true',
  186. help="Test SGX")
  187. parser.add_argument('--simd', default=False, action='store_true',
  188. help="Enable SIMD")
  189. parser.add_argument('--xip', default=False, action='store_true',
  190. help="Enable XIP")
  191. parser.add_argument('--multi-module', default=False, action='store_true',
  192. help="Enable Multi-thread")
  193. parser.add_argument('--multi-thread', default=False, action='store_true',
  194. help="Enable Multi-thread")
  195. parser.add_argument('--gc', default=False, action='store_true',
  196. help='Test with GC')
  197. parser.add_argument('--qemu', default=False, action='store_true',
  198. help="Enable QEMU")
  199. parser.add_argument('--qemu-firmware', default='', help="Firmware required by qemu")
  200. parser.add_argument('--verbose', default=False, action='store_true',
  201. help='show more logs')
  202. # regex patterns of tests to skip
  203. C_SKIP_TESTS = ()
  204. PY_SKIP_TESTS = (
  205. # names.wast
  206. 'invoke \"~!',
  207. # conversions.wast
  208. '18446742974197923840.0',
  209. '18446744073709549568.0',
  210. '9223372036854775808',
  211. 'reinterpret_f.*nan',
  212. # endianness
  213. '.const 0x1.fff' )
  214. def read_forms(string):
  215. forms = []
  216. form = ""
  217. depth = 0
  218. line = 0
  219. pos = 0
  220. while pos < len(string):
  221. # Keep track of line number
  222. if string[pos] == '\n': line += 1
  223. # Handle top-level elements
  224. if depth == 0:
  225. # Add top-level comments
  226. if string[pos:pos+2] == ";;":
  227. end = string.find("\n", pos)
  228. if end == -1: end == len(string)
  229. forms.append(string[pos:end])
  230. pos = end
  231. continue
  232. # TODO: handle nested multi-line comments
  233. if string[pos:pos+2] == "(;":
  234. # Skip multi-line comment
  235. end = string.find(";)", pos)
  236. if end == -1:
  237. raise Exception("mismatch multiline comment on line %d: '%s'" % (
  238. line, string[pos:pos+80]))
  239. pos = end+2
  240. continue
  241. # Ignore whitespace between top-level forms
  242. if string[pos] in (' ', '\n', '\t'):
  243. pos += 1
  244. continue
  245. # Read a top-level form
  246. if string[pos] == '(': depth += 1
  247. if string[pos] == ')': depth -= 1
  248. if depth == 0 and not form:
  249. raise Exception("garbage on line %d: '%s'" % (
  250. line, string[pos:pos+80]))
  251. form += string[pos]
  252. if depth == 0 and form:
  253. forms.append(form)
  254. form = ""
  255. pos += 1
  256. return forms
  257. def get_module_exp_from_assert(string):
  258. depth = 0
  259. pos = 0
  260. module = ""
  261. exception = ""
  262. start_record = False
  263. result = []
  264. while pos < len(string):
  265. # record from the " (module "
  266. if string[pos:pos+7] == "(module":
  267. start_record = True
  268. if start_record:
  269. if string[pos] == '(' : depth += 1
  270. if string[pos] == ')' : depth -= 1
  271. module += string[pos]
  272. # if we get all (module ) .
  273. if depth == 0 and module:
  274. result.append(module)
  275. start_record = False
  276. # get expected exception
  277. if string[pos] == '"':
  278. end = string.find("\"", pos+1)
  279. if end != -1:
  280. end_rel = string.find("\"",end+1)
  281. if end_rel == -1:
  282. result.append(string[pos+1:end])
  283. pos += 1
  284. return result
  285. def string_to_unsigned(number_in_string, lane_type):
  286. if not lane_type in ['i8x16', 'i16x8', 'i32x4', 'i64x2']:
  287. raise Exception("invalid value {} and type {} and lane_type {}".format(number_in_string, type, lane_type))
  288. number = int(number_in_string, 16) if '0x' in number_in_string else int(number_in_string)
  289. if "i8x16" == lane_type:
  290. if number < 0:
  291. packed = struct.pack('b', number)
  292. number = struct.unpack('B', packed)[0]
  293. elif "i16x8" == lane_type:
  294. if number < 0:
  295. packed = struct.pack('h', number)
  296. number = struct.unpack('H', packed)[0]
  297. elif "i32x4" == lane_type:
  298. if number < 0:
  299. packed = struct.pack('i', number)
  300. number = struct.unpack('I', packed)[0]
  301. else: # "i64x2" == lane_type:
  302. if number < 0:
  303. packed = struct.pack('q', number)
  304. number = struct.unpack('Q', packed)[0]
  305. return number
  306. def cast_v128_to_i64x2(numbers, type, lane_type):
  307. numbers = [n.replace("_", "") for n in numbers]
  308. if "i8x16" == lane_type:
  309. assert(16 == len(numbers)), "{} should like {}".format(numbers, lane_type)
  310. # str -> int
  311. numbers = [string_to_unsigned(n, lane_type) for n in numbers]
  312. # i8 -> i64
  313. packed = struct.pack(16 * "B", *numbers)
  314. elif "i16x8" == lane_type:
  315. assert(8 == len(numbers)), "{} should like {}".format(numbers, lane_type)
  316. # str -> int
  317. numbers = [string_to_unsigned(n, lane_type) for n in numbers]
  318. # i16 -> i64
  319. packed = struct.pack(8 * "H", *numbers)
  320. elif "i32x4" == lane_type:
  321. assert(4 == len(numbers)), "{} should like {}".format(numbers, lane_type)
  322. # str -> int
  323. numbers = [string_to_unsigned(n, lane_type) for n in numbers]
  324. # i32 -> i64
  325. packed = struct.pack(4 * "I", *numbers)
  326. elif "i64x2" == lane_type:
  327. assert(2 == len(numbers)), "{} should like {}".format(numbers, lane_type)
  328. # str -> int
  329. numbers = [string_to_unsigned(n, lane_type) for n in numbers]
  330. # i64 -> i64
  331. packed = struct.pack(2 * "Q", *numbers)
  332. elif "f32x4" == lane_type:
  333. assert(4 == len(numbers)), "{} should like {}".format(numbers, lane_type)
  334. # str -> int
  335. numbers = [parse_simple_const_w_type(n, "f32")[0] for n in numbers]
  336. # f32 -> i64
  337. packed = struct.pack(4 * "f", *numbers)
  338. elif "f64x2" == lane_type:
  339. assert(2 == len(numbers)), "{} should like {}".format(numbers, lane_type)
  340. # str -> int
  341. numbers = [parse_simple_const_w_type(n, "f64")[0] for n in numbers]
  342. # f64 -> i64
  343. packed = struct.pack(2 * "d", *numbers)
  344. else:
  345. raise Exception("invalid value {} and type {} and lane_type {}".format(numbers, type, lane_type))
  346. assert(packed)
  347. unpacked = struct.unpack("Q Q", packed)
  348. return unpacked, "[{} {}]:{}:v128".format(unpacked[0], unpacked[1], lane_type)
  349. def parse_simple_const_w_type(number, type):
  350. number = number.replace('_', '')
  351. if type in ["i32", "i64"]:
  352. number = int(number, 16) if '0x' in number else int(number)
  353. return number, "0x{:x}:{}".format(number, type) \
  354. if number >= 0 \
  355. else "-0x{:x}:{}".format(0 - number, type)
  356. elif type in ["f32", "f64"]:
  357. if "nan:" in number:
  358. # TODO: how to handle this correctly
  359. if "nan:canonical" in number:
  360. return float.fromhex("0x200000"), "nan:{}".format(type)
  361. elif "nan:arithmetic" in number:
  362. return float.fromhex("-0x200000"), "nan:{}".format(type)
  363. else:
  364. return float('nan'), "nan:{}".format(type)
  365. else:
  366. number = float.fromhex(number) if '0x' in number else float(number)
  367. return number, "{:.7g}:{}".format(number, type)
  368. elif type == "ref.null":
  369. if number == "func":
  370. return "func", "func:ref.null"
  371. elif number == "extern":
  372. return "extern", "extern:ref.null"
  373. elif number == "any":
  374. return "any", "any:ref.null"
  375. else:
  376. raise Exception("invalid value {} and type {}".format(number, type))
  377. elif type == "ref.extern":
  378. number = int(number, 16) if '0x' in number else int(number)
  379. return number, "0x{:x}:ref.extern".format(number)
  380. elif type == "ref.host":
  381. number = int(number, 16) if '0x' in number else int(number)
  382. return number, "0x{:x}:ref.host".format(number)
  383. else:
  384. raise Exception("invalid value {} and type {}".format(number, type))
  385. def parse_assertion_value(val):
  386. """
  387. Parse something like:
  388. "ref.null extern" in (assert_return (invoke "get-externref" (i32.const 0)) (ref.null extern))
  389. "ref.extern 1" in (assert_return (invoke "get-externref" (i32.const 1)) (ref.extern 1))
  390. "i32.const 0" in (assert_return (invoke "is_null-funcref" (i32.const 1)) (i32.const 0))
  391. in summary:
  392. type.const (sub-type) (val1 val2 val3 val4) ...
  393. type.const val
  394. ref.extern val
  395. ref.null ref_type
  396. ref.array
  397. ref.struct
  398. ref.func
  399. ref.i31
  400. """
  401. if not val:
  402. return None, ""
  403. splitted = re.split('\s+', val)
  404. splitted = [s for s in splitted if s]
  405. type = splitted[0].split(".")[0]
  406. lane_type = splitted[1] if len(splitted) > 2 else ""
  407. numbers = splitted[2:] if len(splitted) > 2 else splitted[1:]
  408. if type in ["i32", "i64", "f32", "f64"]:
  409. return parse_simple_const_w_type(numbers[0], type)
  410. elif type == "ref":
  411. if splitted[0] in ["ref.array", "ref.struct", "ref.func", "ref.i31"]:
  412. return splitted[0]
  413. # need to distinguish between "ref.null" and "ref.extern"
  414. return parse_simple_const_w_type(numbers[0], splitted[0])
  415. else:
  416. return cast_v128_to_i64x2(numbers, type, lane_type)
  417. def int2uint32(i):
  418. return i & 0xffffffff
  419. def int2int32(i):
  420. val = i & 0xffffffff
  421. if val & 0x80000000:
  422. return val - 0x100000000
  423. else:
  424. return val
  425. def int2uint64(i):
  426. return i & 0xffffffffffffffff
  427. def int2int64(i):
  428. val = i & 0xffffffffffffffff
  429. if val & 0x8000000000000000:
  430. return val - 0x10000000000000000
  431. else:
  432. return val
  433. def num_repr(i):
  434. if isinstance(i, int) or isinstance(i, long):
  435. return re.sub("L$", "", hex(i))
  436. else:
  437. return "%.16g" % i
  438. def hexpad16(i):
  439. return "0x%04x" % i
  440. def hexpad24(i):
  441. return "0x%06x" % i
  442. def hexpad32(i):
  443. return "0x%08x" % i
  444. def hexpad64(i):
  445. return "0x%016x" % i
  446. def invoke(r, args, cmd):
  447. r.writeline(cmd)
  448. return r.read_to_prompt(['\r\nwebassembly> ', '\nwebassembly> '],
  449. timeout=args.test_timeout)
  450. def vector_value_comparison(out, expected):
  451. """
  452. out likes "<number number>:v128"
  453. expected likes "[number number]:v128"
  454. """
  455. # print("vector value comparision {} vs {}".format(out, expected))
  456. out_val, out_type = out.split(':')
  457. # <number nubmer> => number number
  458. out_val = out_val[1:-1]
  459. expected_val, lane_type, expected_type = expected.split(':')
  460. # [number nubmer] => number number
  461. expected_val = expected_val[1:-1]
  462. assert("v128" == out_type), "out_type should be v128"
  463. assert("v128" == expected_type), "expected_type should be v128"
  464. if out_type != expected_type:
  465. return False
  466. if out_val == expected_val:
  467. return True
  468. out_val = out_val.split(" ")
  469. expected_val = expected_val.split(" ")
  470. # since i64x2
  471. out_packed = struct.pack("QQ", int(out_val[0], 16), int(out_val[1], 16))
  472. expected_packed = struct.pack("QQ",
  473. int(expected_val[0]) if not "0x" in expected_val[0] else int(expected_val[0], 16),
  474. int(expected_val[1]) if not "0x" in expected_val[1] else int(expected_val[1], 16))
  475. if lane_type in ["i8x16", "i16x8", "i32x4", "i64x2"]:
  476. return out_packed == expected_packed;
  477. else:
  478. assert(lane_type in ["f32x4", "f64x2"]), "unexpected lane_type"
  479. if "f32x4" == lane_type:
  480. out_unpacked = struct.unpack("ffff", out_packed)
  481. expected_unpacked = struct.unpack("ffff", expected_packed)
  482. else:
  483. out_unpacked = struct.unpack("dd", out_packed)
  484. expected_unpacked = struct.unpack("dd", expected_packed)
  485. out_is_nan = [math.isnan(o) for o in out_unpacked]
  486. expected_is_nan = [math.isnan(e) for e in expected_unpacked]
  487. if out_is_nan and expected_is_nan:
  488. return True;
  489. # print("compare {} and {}".format(out_unpacked, expected_unpacked))
  490. result = [o == e for o, e in zip(out_unpacked, expected_unpacked)]
  491. if not all(result):
  492. result = [
  493. "{:.7g}".format(o) == "{:.7g}".format(e)
  494. for o, e in zip(out_unpacked, expected_packed)
  495. ]
  496. return all(result)
  497. def simple_value_comparison(out, expected):
  498. """
  499. compare out of simple types which may like val:i32, val:f64 and so on
  500. """
  501. if expected == "2.360523e+13:f32" and out == "2.360522e+13:f32":
  502. # one case in float_literals.wast, due to float precision of python
  503. return True
  504. if expected == "1.797693e+308:f64" and out == "inf:f64":
  505. # one case in float_misc.wast:
  506. # (assert_return (invoke "f64.add" (f64.const 0x1.fffffffffffffp+1023)
  507. # (f64.const 0x1.fffffffffffffp+969))
  508. # (f64.const 0x1.fffffffffffffp+1023))
  509. # the add result in x86_32 is inf
  510. return True
  511. out_val, out_type = out.split(':')
  512. expected_val, expected_type = expected.split(':')
  513. if not out_type == expected_type:
  514. return False
  515. out_val, _ = parse_simple_const_w_type(out_val, out_type)
  516. expected_val, _ = parse_simple_const_w_type(expected_val, expected_type)
  517. if out_val == expected_val \
  518. or (math.isnan(out_val) and math.isnan(expected_val)):
  519. return True
  520. if "i32" == expected_type:
  521. out_val_binary = struct.pack('I', out_val) if out_val > 0 \
  522. else struct.pack('i', out_val)
  523. expected_val_binary = struct.pack('I', expected_val) \
  524. if expected_val > 0 \
  525. else struct.pack('i', expected_val)
  526. elif "i64" == expected_type:
  527. out_val_binary = struct.pack('Q', out_val) if out_val > 0 \
  528. else struct.pack('q', out_val)
  529. expected_val_binary = struct.pack('Q', expected_val) \
  530. if expected_val > 0 \
  531. else struct.pack('q', expected_val)
  532. elif "f32" == expected_type:
  533. out_val_binary = struct.pack('f', out_val)
  534. expected_val_binary = struct.pack('f', expected_val)
  535. elif "f64" == expected_type:
  536. out_val_binary = struct.pack('d', out_val)
  537. expected_val_binary = struct.pack('d', expected_val)
  538. elif "ref.extern" == expected_type:
  539. out_val_binary = out_val
  540. expected_val_binary = expected_val
  541. elif "ref.host" == expected_type:
  542. out_val_binary = out_val
  543. expected_val_binary = expected_val
  544. else:
  545. assert(0), "unknown 'expected_type' {}".format(expected_type)
  546. if out_val_binary == expected_val_binary:
  547. return True
  548. if expected_type in ["f32", "f64"]:
  549. # compare with a lower precision
  550. out_str = "{:.7g}".format(out_val)
  551. expected_str = "{:.7g}".format(expected_val)
  552. if out_str == expected_str:
  553. return True
  554. return False
  555. def value_comparison(out, expected):
  556. if out == expected:
  557. return True
  558. if not expected:
  559. return False
  560. if not out in ["ref.array", "ref.struct", "ref.func", "ref.any", "ref.i31"]:
  561. assert(':' in out), "out should be in a form likes numbers:type, but {}".format(out)
  562. if not expected in ["ref.array", "ref.struct", "ref.func", "ref.any", "ref.i31"]:
  563. assert(':' in expected), "expected should be in a form likes numbers:type, but {}".format(expected)
  564. if 'v128' in out:
  565. return vector_value_comparison(out, expected)
  566. else:
  567. return simple_value_comparison(out, expected)
  568. def is_result_match_expected(out, expected):
  569. # compare value instead of comparing strings of values
  570. return value_comparison(out, expected)
  571. def test_assert(r, opts, mode, cmd, expected):
  572. log("Testing(%s) %s = %s" % (mode, cmd, expected))
  573. out = invoke(r, opts, cmd)
  574. if '\n' in out or ' ' in out:
  575. outs = [''] + out.split('\n')[1:]
  576. out = outs[-1]
  577. if mode=='trap':
  578. o = re.sub('^Exception: ', '', out)
  579. e = re.sub('^Exception: ', '', expected)
  580. if o.find(e) >= 0 or e.find(o) >= 0:
  581. return True
  582. if mode=='exhaustion':
  583. o = re.sub('^Exception: ', '', out)
  584. expected = 'Exception: stack overflow'
  585. e = re.sub('^Exception: ', '', expected)
  586. if o.find(e) >= 0 or e.find(o) >= 0:
  587. return True
  588. ## 0x9:i32,-0x1:i32 -> ['0x9:i32', '-0x1:i32']
  589. expected_list = re.split(',', expected)
  590. out_list = re.split(',', out)
  591. if len(expected_list) != len(out_list):
  592. raise Exception("Failed:\n Results count incorrect:\n expected: '%s'\n got: '%s'" % (expected, out))
  593. for i in range(len(expected_list)):
  594. if not is_result_match_expected(out_list[i], expected_list[i]):
  595. raise Exception("Failed:\n Result %d incorrect:\n expected: '%s'\n got: '%s'" % (i, expected_list[i], out_list[i]))
  596. return True
  597. def test_assert_return(r, opts, form):
  598. """
  599. m. to search a pattern like (assert_return (invoke function_name ... ) ...)
  600. n. to search a pattern like (assert_return (invoke $module_name function_name ... ) ...)
  601. """
  602. # params, return
  603. m = re.search('^\(assert_return\s+\(invoke\s+"((?:[^"]|\\\")*)"\s+(\(.*\))\s*\)\s*(\(.*\))\s*\)\s*$', form, re.S)
  604. # judge if assert_return cmd includes the module name
  605. n = re.search('^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"((?:[^"]|\\\")*)"\s+(\(.*\))\s*\)\s*(\(.*\))\s*\)\s*$', form, re.S)
  606. # print("assert_return with {}".format(form))
  607. if not m:
  608. # no params, return
  609. m = re.search('^\(assert_return\s+\(invoke\s+"((?:[^"]|\\\")*)"\s*\)\s+()(\(.*\))\s*\)\s*$', form, re.S)
  610. if not m:
  611. # params, no return
  612. m = re.search('^\(assert_return\s+\(invoke\s+"([^"]*)"\s+(\(.*\))()\s*\)\s*\)\s*$', form, re.S)
  613. if not m:
  614. # no params, no return
  615. m = re.search('^\(assert_return\s+\(invoke\s+"([^"]*)"\s*()()\)\s*\)\s*$', form, re.S)
  616. if not m:
  617. # params, return
  618. if not n:
  619. # no params, return
  620. n = re.search('^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"((?:[^"]|\\\")*)"\s*\)\s+()(\(.*\))\s*\)\s*$', form, re.S)
  621. if not n:
  622. # params, no return
  623. n = re.search('^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"\s+(\(.*\))()\s*\)\s*\)\s*$', form, re.S)
  624. if not n:
  625. # no params, no return
  626. n = re.search('^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"*()()\)\s*\)\s*$', form, re.S)
  627. if not m and not n:
  628. if re.search('^\(assert_return\s+\(get.*\).*\)$', form, re.S):
  629. log("ignoring assert_return get");
  630. return
  631. else:
  632. raise Exception("unparsed assert_return: '%s'" % form)
  633. if m and not n:
  634. func = m.group(1)
  635. if ' ' in func:
  636. func = func.replace(' ', '\\')
  637. if m.group(2) == '':
  638. args = []
  639. else:
  640. #args = [re.split(' +', v)[1].replace('_', "") for v in re.split("\)\s*\(", m.group(2)[1:-1])]
  641. # split arguments with ')spaces(', remove leading and tailing ) and (
  642. args_type_and_value = re.split(r'\)\s+\(', m.group(2)[1:-1])
  643. args_type_and_value = [s.replace('_', '') for s in args_type_and_value]
  644. # args are in two forms:
  645. # f32.const -0x1.000001fffffffffffp-50
  646. # v128.const i32x4 0 0 0 0
  647. args = []
  648. for arg in args_type_and_value:
  649. # remove leading and tailing spaces, it might confuse following assertions
  650. arg = arg.strip()
  651. splitted = re.split('\s+', arg)
  652. splitted = [s for s in splitted if s]
  653. if splitted[0] in ["i32.const", "i64.const"]:
  654. assert(2 == len(splitted)), "{} should have two parts".format(splitted)
  655. # in wast 01234 means 1234
  656. # in c 0123 means 83 in oct
  657. number, _ = parse_simple_const_w_type(splitted[1], splitted[0][:3])
  658. args.append(str(number))
  659. elif splitted[0] in ["f32.const", "f64.const"]:
  660. # let strtof or strtod handle original arguments
  661. assert(2 == len(splitted)), "{} should have two parts".format(splitted)
  662. args.append(splitted[1])
  663. elif "v128.const" == splitted[0]:
  664. assert(len(splitted) > 2), "{} should have more than two parts".format(splitted)
  665. numbers, _ = cast_v128_to_i64x2(splitted[2:], 'v128', splitted[1])
  666. assert(len(numbers) == 2), "has to reform arguments into i64x2"
  667. args.append("{}\{}".format(numbers[0], numbers[1]))
  668. elif "ref.null" == splitted[0]:
  669. args.append("null")
  670. elif "ref.extern" == splitted[0]:
  671. number, _ = parse_simple_const_w_type(splitted[1], splitted[0])
  672. args.append(str(number))
  673. elif "ref.host" == splitted[0]:
  674. number, _ = parse_simple_const_w_type(splitted[1], splitted[0])
  675. args.append(str(number))
  676. else:
  677. assert(0), "an unkonwn parameter type"
  678. if m.group(3) == '':
  679. returns= []
  680. else:
  681. returns = re.split("\)\s*\(", m.group(3)[1:-1])
  682. # processed numbers in strings
  683. if len(returns) == 1 and returns[0] in ["ref.array", "ref.struct", "ref.i31",
  684. "ref.eq", "ref.any", "ref.extern",
  685. "ref.func", "ref.null"]:
  686. expected = [returns[0]]
  687. elif len(returns) == 1 and returns[0] in ["func:ref.null", "any:ref.null",
  688. "extern:ref.null"]:
  689. expected = [returns[0]]
  690. else:
  691. expected = [parse_assertion_value(v)[1] for v in returns]
  692. expected = ",".join(expected)
  693. test_assert(r, opts, "return", "%s %s" % (func, " ".join(args)), expected)
  694. elif not m and n:
  695. module = temp_module_table[n.group(1)].split(".wasm")[0]
  696. # assume the cmd is (assert_return(invoke $ABC "func")).
  697. # run the ABC.wasm firstly
  698. if test_aot:
  699. r = compile_wasm_to_aot(module+".wasm", module+".aot", True, opts, r)
  700. try:
  701. assert_prompt(r, ['Compile success'], opts.start_timeout, False)
  702. except:
  703. _, exc, _ = sys.exc_info()
  704. log("Run wamrc failed:\n got: '%s'" % r.buf)
  705. sys.exit(1)
  706. r = run_wasm_with_repl(module+".wasm", module+".aot" if test_aot else module, opts, r)
  707. # Wait for the initial prompt
  708. try:
  709. assert_prompt(r, ['webassembly> '], opts.start_timeout, False)
  710. except:
  711. _, exc, _ = sys.exc_info()
  712. raise Exception("Failed:\n expected: '%s'\n got: '%s'" % \
  713. (repr(exc), r.buf))
  714. func = n.group(2)
  715. if ' ' in func:
  716. func = func.replace(' ', '\\')
  717. if n.group(3) == '':
  718. args=[]
  719. else:
  720. # convert (ref.null extern/func) into (ref.null null)
  721. n1 = n.group(3).replace("(ref.null extern)", "(ref.null null)")
  722. n1 = n1.replace("ref.null func)", "(ref.null null)")
  723. args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", n1[1:-1])]
  724. _, expected = parse_assertion_value(n.group(4)[1:-1])
  725. test_assert(r, opts, "return", "%s %s" % (func, " ".join(args)), expected)
  726. def test_assert_trap(r, opts, form):
  727. # params
  728. m = re.search('^\(assert_trap\s+\(invoke\s+"([^"]*)"\s+(\(.*\))\s*\)\s*"([^"]+)"\s*\)\s*$', form)
  729. # judge if assert_return cmd includes the module name
  730. n = re.search('^\(assert_trap\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"\s+(\(.*\))\s*\)\s*"([^"]+)"\s*\)\s*$', form, re.S)
  731. if not m:
  732. # no params
  733. m = re.search('^\(assert_trap\s+\(invoke\s+"([^"]*)"\s*()\)\s*"([^"]+)"\s*\)\s*$', form)
  734. if not m:
  735. if not n:
  736. # no params
  737. n = re.search('^\(assert_trap\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"\s*()\)\s*"([^"]+)"\s*\)\s*$', form, re.S)
  738. if not m and not n:
  739. raise Exception("unparsed assert_trap: '%s'" % form)
  740. if m and not n:
  741. func = m.group(1)
  742. if m.group(2) == '':
  743. args = []
  744. else:
  745. # convert (ref.null extern/func) into (ref.null null)
  746. m1 = m.group(2).replace("(ref.null extern)", "(ref.null null)")
  747. m1 = m1.replace("ref.null func)", "(ref.null null)")
  748. args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m1[1:-1])]
  749. expected = "Exception: %s" % m.group(3)
  750. test_assert(r, opts, "trap", "%s %s" % (func, " ".join(args)), expected)
  751. elif not m and n:
  752. module = n.group(1)
  753. module = tempfile.gettempdir() + "/" + module
  754. # will trigger the module named in assert_return(invoke $ABC).
  755. # run the ABC.wasm firstly
  756. if test_aot:
  757. r = compile_wasm_to_aot(module+".wasm", module+".aot", True, opts, r)
  758. try:
  759. assert_prompt(r, ['Compile success'], opts.start_timeout, False)
  760. except:
  761. _, exc, _ = sys.exc_info()
  762. log("Run wamrc failed:\n got: '%s'" % r.buf)
  763. sys.exit(1)
  764. r = run_wasm_with_repl(module+".wasm", module+".aot" if test_aot else module, opts, r)
  765. # Wait for the initial prompt
  766. try:
  767. assert_prompt(r, ['webassembly> '], opts.start_timeout, False)
  768. except:
  769. _, exc, _ = sys.exc_info()
  770. raise Exception("Failed:\n expected: '%s'\n got: '%s'" % \
  771. (repr(exc), r.buf))
  772. func = n.group(2)
  773. if n.group(3) == '':
  774. args = []
  775. else:
  776. args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", n.group(3)[1:-1])]
  777. expected = "Exception: %s" % n.group(4)
  778. test_assert(r, opts, "trap", "%s %s" % (func, " ".join(args)), expected)
  779. def test_assert_exhaustion(r,opts,form):
  780. # params
  781. m = re.search('^\(assert_exhaustion\s+\(invoke\s+"([^"]*)"\s+(\(.*\))\s*\)\s*"([^"]+)"\s*\)\s*$', form)
  782. if not m:
  783. # no params
  784. m = re.search('^\(assert_exhaustion\s+\(invoke\s+"([^"]*)"\s*()\)\s*"([^"]+)"\s*\)\s*$', form)
  785. if not m:
  786. raise Exception("unparsed assert_exhaustion: '%s'" % form)
  787. func = m.group(1)
  788. if m.group(2) == '':
  789. args = []
  790. else:
  791. args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m.group(2)[1:-1])]
  792. expected = "Exception: %s\n" % m.group(3)
  793. test_assert(r, opts, "exhaustion", "%s %s" % (func, " ".join(args)), expected)
  794. def do_invoke(r, opts, form):
  795. # params
  796. m = re.search('^\(invoke\s+"([^"]+)"\s+(\(.*\))\s*\)\s*$', form)
  797. if not m:
  798. # no params
  799. m = re.search('^\(invoke\s+"([^"]+)"\s*()\)\s*$', form)
  800. if not m:
  801. raise Exception("unparsed invoke: '%s'" % form)
  802. func = m.group(1)
  803. if ' ' in func:
  804. func = func.replace(' ', '\\')
  805. if m.group(2) == '':
  806. args = []
  807. else:
  808. args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m.group(2)[1:-1])]
  809. log("Invoking %s(%s)" % (
  810. func, ", ".join([str(a) for a in args])))
  811. invoke(r, opts, "%s %s" % (func, " ".join(args)))
  812. def skip_test(form, skip_list):
  813. for s in skip_list:
  814. if re.search(s, form):
  815. return True
  816. return False
  817. def compile_wast_to_wasm(form, wast_tempfile, wasm_tempfile, opts):
  818. log("Writing WAST module to '%s'" % wast_tempfile)
  819. open(wast_tempfile, 'w').write(form)
  820. log("Compiling WASM to '%s'" % wasm_tempfile)
  821. # default arguments
  822. if opts.gc:
  823. cmd = [opts.wast2wasm, "-u", "-d", wast_tempfile, "-o", wasm_tempfile]
  824. else:
  825. cmd = [opts.wast2wasm, "--enable-thread", "--no-check",
  826. wast_tempfile, "-o", wasm_tempfile ]
  827. # remove reference-type and bulk-memory enabling options since a WABT
  828. # commit 30c1e983d30b33a8004b39fd60cbd64477a7956c
  829. # Enable reference types by default (#1729)
  830. log("Running: %s" % " ".join(cmd))
  831. try:
  832. subprocess.check_call(cmd)
  833. except subprocess.CalledProcessError as e:
  834. print(str(e))
  835. return False
  836. return True
  837. def compile_wasm_to_aot(wasm_tempfile, aot_tempfile, runner, opts, r, output = 'default'):
  838. log("Compiling AOT to '%s'" % aot_tempfile)
  839. cmd = [opts.aot_compiler]
  840. if test_target == "x86_64":
  841. cmd.append("--target=x86_64")
  842. cmd.append("--cpu=skylake")
  843. elif test_target == "i386":
  844. cmd.append("--target=i386")
  845. elif test_target == "aarch64":
  846. cmd += ["--target=aarch64", "--cpu=cortex-a57"]
  847. elif test_target == "armv7":
  848. cmd += ["--target=armv7", "--target-abi=gnueabihf"]
  849. elif test_target == "thumbv7":
  850. cmd += ["--target=thumbv7", "--target-abi=gnueabihf", "--cpu=cortex-a9", "--cpu-features=-neon"]
  851. elif test_target == "riscv32_ilp32":
  852. cmd += ["--target=riscv32", "--target-abi=ilp32", "--cpu=generic-rv32", "--cpu-features=+m,+a,+c"]
  853. elif test_target == "riscv32_ilp32d":
  854. cmd += ["--target=riscv32", "--target-abi=ilp32d", "--cpu=generic-rv32", "--cpu-features=+m,+a,+c"]
  855. elif test_target == "riscv64_lp64":
  856. cmd += ["--target=riscv64", "--target-abi=lp64", "--cpu=generic-rv64", "--cpu-features=+m,+a,+c"]
  857. elif test_target == "riscv64_lp64d":
  858. cmd += ["--target=riscv64", "--target-abi=lp64d", "--cpu=generic-rv32", "--cpu-features=+m,+a,+c"]
  859. else:
  860. pass
  861. if opts.sgx:
  862. cmd.append("-sgx")
  863. if not opts.simd:
  864. cmd.append("--disable-simd")
  865. if opts.xip:
  866. cmd.append("--enable-indirect-mode")
  867. cmd.append("--disable-llvm-intrinsics")
  868. if opts.multi_thread:
  869. cmd.append("--enable-multi-thread")
  870. if output == 'object':
  871. cmd.append("--format=object")
  872. elif output == 'ir':
  873. cmd.append("--format=llvmir-opt")
  874. # disable llvm link time optimization as it might convert
  875. # code of tail call into code of dead loop, and stack overflow
  876. # exception isn't thrown in several cases
  877. cmd.append("--disable-llvm-lto")
  878. cmd += ["-o", aot_tempfile, wasm_tempfile]
  879. log("Running: %s" % " ".join(cmd))
  880. if not runner:
  881. subprocess.check_call(cmd)
  882. else:
  883. if (r != None):
  884. r.cleanup()
  885. r = Runner(cmd, no_pty=opts.no_pty)
  886. return r
  887. def run_wasm_with_repl(wasm_tempfile, aot_tempfile, opts, r):
  888. tmpfile = aot_tempfile if test_aot else wasm_tempfile
  889. log("Starting interpreter for module '%s'" % tmpfile)
  890. cmd_iwasm = [opts.interpreter, "--heap-size=0", "-v=5" if opts.verbose else "-v=0", "--repl", tmpfile]
  891. if opts.multi_module:
  892. cmd_iwasm.insert(1, "--module-path=" + (tempfile.gettempdir() if not opts.qemu else "/tmp" ))
  893. if opts.qemu:
  894. if opts.qemu_firmware == '':
  895. raise Exception("QEMU firmware missing")
  896. if opts.target == "thumbv7":
  897. cmd = ["qemu-system-arm", "-semihosting", "-M", "sabrelite", "-m", "1024", "-smp", "4", "-nographic", "-kernel", opts.qemu_firmware]
  898. elif opts.target == "riscv32_ilp32":
  899. cmd = ["qemu-system-riscv32", "-semihosting", "-M", "virt,aclint=on", "-cpu", "rv32", "-smp", "8", "-nographic", "-bios", "none", "-kernel", opts.qemu_firmware]
  900. elif opts.target == "riscv64_lp64":
  901. cmd = ["qemu-system-riscv64", "-semihosting", "-M", "virt,aclint=on", "-cpu", "rv64", "-smp", "8", "-nographic", "-bios", "none", "-kernel", opts.qemu_firmware]
  902. else:
  903. cmd = cmd_iwasm
  904. log("Running: %s" % " ".join(cmd))
  905. if (r != None):
  906. r.cleanup()
  907. r = Runner(cmd, no_pty=opts.no_pty)
  908. if opts.qemu:
  909. r.read_to_prompt(['nsh> '], 10)
  910. r.writeline("mount -t hostfs -o fs={} /tmp".format(tempfile.gettempdir()))
  911. r.read_to_prompt(['nsh> '], 10)
  912. r.writeline(" ".join(cmd_iwasm))
  913. return r
  914. def create_tmpfiles(wast_name):
  915. tempfiles = []
  916. (t1fd, wast_tempfile) = tempfile.mkstemp(suffix=".wast")
  917. (t2fd, wasm_tempfile) = tempfile.mkstemp(suffix=".wasm")
  918. tempfiles.append(wast_tempfile)
  919. tempfiles.append(wasm_tempfile)
  920. if test_aot:
  921. (t3fd, aot_tempfile) = tempfile.mkstemp(suffix=".aot")
  922. tempfiles.append(aot_tempfile)
  923. # add these temp file to temporal repo, will be deleted when finishing the test
  924. temp_file_repo.extend(tempfiles)
  925. return tempfiles
  926. def test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile, opts, r, loadable = True):
  927. details_inside_ast = get_module_exp_from_assert(form)
  928. log("module is ....'%s'"%details_inside_ast[0])
  929. log("exception is ....'%s'"%details_inside_ast[1])
  930. # parse the module
  931. module = details_inside_ast[0]
  932. expected = details_inside_ast[1]
  933. if not compile_wast_to_wasm(module, wast_tempfile, wasm_tempfile, opts):
  934. raise Exception("compile wast to wasm failed")
  935. if test_aot:
  936. r = compile_wasm_to_aot(wasm_tempfile, aot_tempfile, True, opts, r)
  937. try:
  938. assert_prompt(r, ['Compile success'], opts.start_timeout, True)
  939. except:
  940. _, exc, _ = sys.exc_info()
  941. if (r.buf.find(expected) >= 0):
  942. log("Out exception includes expected one, pass:")
  943. log(" Expected: %s" % expected)
  944. log(" Got: %s" % r.buf)
  945. return
  946. else:
  947. log("Run wamrc failed:\n expected: '%s'\n got: '%s'" % \
  948. (expected, r.buf))
  949. sys.exit(1)
  950. r = run_wasm_with_repl(wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
  951. # Some module couldn't load so will raise an error directly, so shell prompt won't show here
  952. if loadable:
  953. # Wait for the initial prompt
  954. try:
  955. assert_prompt(r, ['webassembly> '], opts.start_timeout, True)
  956. except:
  957. _, exc, _ = sys.exc_info()
  958. if (r.buf.find(expected) >= 0):
  959. log("Out exception includes expected one, pass:")
  960. log(" Expected: %s" %expected)
  961. log(" Got: %s" % r.buf)
  962. else:
  963. raise Exception("Failed:\n expected: '%s'\n got: '%s'" % \
  964. (expected, r.buf))
  965. if __name__ == "__main__":
  966. opts = parser.parse_args(sys.argv[1:])
  967. print('Input param :',opts)
  968. if opts.aot: test_aot = True
  969. # default x86_64
  970. test_target = opts.target
  971. if opts.rundir: os.chdir(opts.rundir)
  972. if opts.log_file: log_file = open(opts.log_file, "a")
  973. if opts.debug_file: debug_file = open(opts.debug_file, "a")
  974. if opts.interpreter.endswith(".py"):
  975. SKIP_TESTS = PY_SKIP_TESTS
  976. else:
  977. SKIP_TESTS = C_SKIP_TESTS
  978. (t1fd, wast_tempfile) = tempfile.mkstemp(suffix=".wast")
  979. (t2fd, wasm_tempfile) = tempfile.mkstemp(suffix=".wasm")
  980. if test_aot:
  981. (t3fd, aot_tempfile) = tempfile.mkstemp(suffix=".aot")
  982. ret_code = 0
  983. try:
  984. log("################################################")
  985. log("### Testing %s" % opts.test_file.name)
  986. log("################################################")
  987. forms = read_forms(opts.test_file.read())
  988. r = None
  989. for form in forms:
  990. # log("\n### Current Case is " + form + "\n")
  991. if ";;" == form[0:2]:
  992. log(form)
  993. elif skip_test(form, SKIP_TESTS):
  994. log("Skipping test: %s" % form[0:60])
  995. elif re.match("^\(assert_trap\s+\(module", form):
  996. test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
  997. elif re.match("^\(assert_exhaustion\\b.*", form):
  998. test_assert_exhaustion(r, opts, form)
  999. elif re.match("^\(assert_unlinkable\\b.*", form):
  1000. test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile if test_aot else None, opts, r, False)
  1001. elif re.match("^\(assert_malformed\\b.*", form):
  1002. # remove comments in wast
  1003. form,n = re.subn(";;.*\n", "", form)
  1004. m = re.match("^\(assert_malformed\s*\(module binary\s*(\".*\").*\)\s*\"(.*)\"\s*\)$", form, re.DOTALL)
  1005. if m:
  1006. # workaround: spec test changes error message to "malformed" while iwasm still use "invalid"
  1007. error_msg = m.group(2).replace("malformed", "invalid")
  1008. log("Testing(malformed)")
  1009. f = open(wasm_tempfile, 'wb')
  1010. s = m.group(1)
  1011. while s:
  1012. res = re.match("[^\"]*\"([^\"]*)\"(.*)", s, re.DOTALL)
  1013. if IS_PY_3:
  1014. context = res.group(1).replace("\\", "\\x").encode("latin1").decode("unicode-escape").encode("latin1")
  1015. f.write(context)
  1016. else:
  1017. f.write(res.group(1).replace("\\", "\\x").decode("string-escape"))
  1018. s = res.group(2)
  1019. f.close()
  1020. # compile wasm to aot
  1021. if test_aot:
  1022. r = compile_wasm_to_aot(wasm_tempfile, aot_tempfile, True, opts, r)
  1023. try:
  1024. assert_prompt(r, ['Compile success'], opts.start_timeout, True)
  1025. except:
  1026. _, exc, _ = sys.exc_info()
  1027. if (r.buf.find(error_msg) >= 0):
  1028. log("Out exception includes expected one, pass:")
  1029. log(" Expected: %s" % error_msg)
  1030. log(" Got: %s" % r.buf)
  1031. else:
  1032. log("Run wamrc failed:\n expected: '%s'\n got: '%s'" % \
  1033. (error_msg, r.buf))
  1034. continue
  1035. r = run_wasm_with_repl(wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
  1036. if (error_msg == "unexpected end of section or function"):
  1037. # one case in binary.wast
  1038. assert_prompt(r, ["unexpected end", error_msg], opts.start_timeout, True)
  1039. elif (error_msg == "invalid value type"):
  1040. # one case in binary.wast
  1041. assert_prompt(r, ["unexpected end", error_msg], opts.start_timeout, True)
  1042. elif (error_msg == "length out of bounds"):
  1043. # one case in custom.wast
  1044. assert_prompt(r, ["unexpected end", error_msg], opts.start_timeout, True)
  1045. elif (error_msg == "integer representation too long"):
  1046. # several cases in binary-leb128.wast
  1047. assert_prompt(r, ["invalid section id", error_msg], opts.start_timeout, True)
  1048. elif re.match("^\(assert_malformed\s*\(module quote", form):
  1049. log("ignoring assert_malformed module quote")
  1050. else:
  1051. log("unrecognized assert_malformed")
  1052. elif re.match("^\(assert_return[_a-z]*_nan\\b.*", form):
  1053. log("ignoring assert_return_.*_nan")
  1054. pass
  1055. elif re.match(".*\(invoke\s+\$\\b.*", form):
  1056. # invoke a particular named module's function
  1057. if form.startswith("(assert_return"):
  1058. test_assert_return(r,opts,form)
  1059. elif form.startswith("(assert_trap"):
  1060. test_assert_trap(r,opts,form)
  1061. elif re.match("^\(module\\b.*", form):
  1062. # if the module includes the particular name startswith $
  1063. m = re.search("^\(module\s+\$.\S+", form)
  1064. if m:
  1065. # get module name
  1066. module_name = re.split('\$', m.group(0).strip())[1]
  1067. if module_name:
  1068. # create temporal files
  1069. temp_files = create_tmpfiles(module_name)
  1070. if not compile_wast_to_wasm(form, temp_files[0], temp_files[1], opts):
  1071. raise Exception("compile wast to wasm failed")
  1072. if test_aot:
  1073. r = compile_wasm_to_aot(temp_files[1], temp_files[2], True, opts, r)
  1074. try:
  1075. assert_prompt(r, ['Compile success'], opts.start_timeout, False)
  1076. except:
  1077. _, exc, _ = sys.exc_info()
  1078. log("Run wamrc failed:\n got: '%s'" % r.buf)
  1079. sys.exit(1)
  1080. temp_module_table[module_name] = temp_files[1]
  1081. r = run_wasm_with_repl(temp_files[1], temp_files[2] if test_aot else None, opts, r)
  1082. else:
  1083. if not compile_wast_to_wasm(form, wast_tempfile, wasm_tempfile, opts):
  1084. raise Exception("compile wast to wasm failed")
  1085. if test_aot:
  1086. r = compile_wasm_to_aot(wasm_tempfile, aot_tempfile, True, opts, r)
  1087. try:
  1088. assert_prompt(r, ['Compile success'], opts.start_timeout, False)
  1089. except:
  1090. _, exc, _ = sys.exc_info()
  1091. log("Run wamrc failed:\n got: '%s'" % r.buf)
  1092. sys.exit(1)
  1093. r = run_wasm_with_repl(wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
  1094. # Wait for the initial prompt
  1095. try:
  1096. assert_prompt(r, ['webassembly> '], opts.start_timeout, False)
  1097. except:
  1098. _, exc, _ = sys.exc_info()
  1099. raise Exception("Failed:\n expected: '%s'\n got: '%s'" % \
  1100. (repr(exc), r.buf))
  1101. elif re.match("^\(assert_return\\b.*", form):
  1102. assert(r), "iwasm repl runtime should be not null"
  1103. test_assert_return(r, opts, form)
  1104. elif re.match("^\(assert_trap\\b.*", form):
  1105. test_assert_trap(r, opts, form)
  1106. elif re.match("^\(invoke\\b.*", form):
  1107. assert(r), "iwasm repl runtime should be not null"
  1108. do_invoke(r, opts, form)
  1109. elif re.match("^\(assert_invalid\\b.*", form):
  1110. test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
  1111. elif re.match("^\(register\\b.*", form):
  1112. # get module's new name from the register cmd
  1113. name_new =re.split('\"',re.search('\".*\"',form).group(0))[1]
  1114. if name_new:
  1115. new_module = os.path.join(tempfile.gettempdir(), name_new + ".wasm")
  1116. shutil.copyfile(temp_module_table.get(name_new, wasm_tempfile), new_module)
  1117. # add new_module copied from the old into temp_file_repo[]
  1118. temp_file_repo.append(new_module)
  1119. else:
  1120. # there is no name defined in register cmd
  1121. raise Exception("can not find module name from the register")
  1122. else:
  1123. raise Exception("unrecognized form '%s...'" % form[0:40])
  1124. except Exception as e:
  1125. traceback.print_exc()
  1126. print("THE FINAL EXCEPTION IS {}".format(e))
  1127. ret_code = 101
  1128. shutil.copyfile(wasm_tempfile, os.path.join(opts.log_dir, os.path.basename(wasm_tempfile)))
  1129. if opts.aot or opts.xip:
  1130. shutil.copyfile(aot_tempfile, os.path.join(opts.log_dir,os.path.basename(aot_tempfile)))
  1131. if "indirect-mode" in str(e):
  1132. compile_wasm_to_aot(wasm_tempfile, aot_tempfile, None, opts, None, "object")
  1133. shutil.copyfile(aot_tempfile, os.path.join(opts.log_dir,os.path.basename(aot_tempfile)+'.o'))
  1134. subprocess.check_call(["llvm-objdump", "-r", aot_tempfile])
  1135. compile_wasm_to_aot(wasm_tempfile, aot_tempfile, None, opts, None, "ir")
  1136. shutil.copyfile(aot_tempfile, os.path.join(opts.log_dir,os.path.basename(aot_tempfile)+".ir"))
  1137. else:
  1138. ret_code = 0
  1139. finally:
  1140. if not opts.no_cleanup:
  1141. log("Removing tempfiles")
  1142. os.remove(wast_tempfile)
  1143. os.remove(wasm_tempfile)
  1144. if test_aot:
  1145. os.remove(aot_tempfile)
  1146. # remove the files under /tempfiles/ and copy of .wasm files
  1147. if temp_file_repo:
  1148. for t in temp_file_repo:
  1149. if(len(str(t))!=0 and os.path.exists(t)):
  1150. os.remove(t)
  1151. log("### End testing %s" % opts.test_file.name)
  1152. else:
  1153. log("Leaving tempfiles: %s" % ([wast_tempfile, wasm_tempfile]))
  1154. sys.exit(ret_code)