main.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. /*
  2. * Copyright (C) 2019 Intel Corporation. All rights reserved.
  3. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. */
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include "bh_platform.h"
  8. #include "bh_read_file.h"
  9. #include "wasm_export.h"
  10. #if WASM_ENABLE_LIBC_WASI != 0
  11. #include "../common/libc_wasi.c"
  12. #endif
  13. static int app_argc;
  14. static char **app_argv;
  15. #define MODULE_PATH ("--module-path=")
  16. /* clang-format off */
  17. static int
  18. print_help()
  19. {
  20. printf("Usage: iwasm [-options] wasm_file [args...]\n");
  21. printf("options:\n");
  22. printf(" -f|--function name Specify a function name of the module to run rather\n"
  23. " than main\n");
  24. #if WASM_ENABLE_LOG != 0
  25. printf(" -v=n Set log verbose level (0 to 5, default is 2) larger\n"
  26. " level with more log\n");
  27. #endif
  28. #if WASM_ENABLE_INTERP != 0
  29. printf(" --interp Run the wasm app with interpreter mode\n");
  30. #endif
  31. #if WASM_ENABLE_FAST_JIT != 0
  32. printf(" --fast-jit Run the wasm app with fast jit mode\n");
  33. #endif
  34. #if WASM_ENABLE_JIT != 0
  35. printf(" --llvm-jit Run the wasm app with llvm jit mode\n");
  36. #endif
  37. #if WASM_ENABLE_JIT != 0 && WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0
  38. printf(" --multi-tier-jit Run the wasm app with multi-tier jit mode\n");
  39. #endif
  40. printf(" --stack-size=n Set maximum stack size in bytes, default is 64 KB\n");
  41. printf(" --heap-size=n Set maximum heap size in bytes, default is 16 KB\n");
  42. #if WASM_ENABLE_GC != 0
  43. printf(" --gc-heap-size=n Set maximum gc heap size in bytes,\n");
  44. printf(" default is %u KB\n", GC_HEAP_SIZE_DEFAULT / 1024);
  45. #endif
  46. #if WASM_ENABLE_JIT != 0
  47. printf(" --llvm-jit-size-level=n Set LLVM JIT size level, default is 3\n");
  48. printf(" --llvm-jit-opt-level=n Set LLVM JIT optimization level, default is 3\n");
  49. #endif
  50. printf(" --repl Start a very simple REPL (read-eval-print-loop) mode\n"
  51. " that runs commands in the form of `FUNC ARG...`\n");
  52. #if WASM_ENABLE_LIBC_WASI != 0
  53. libc_wasi_print_help();
  54. #endif
  55. #if WASM_ENABLE_MULTI_MODULE != 0
  56. printf(" --module-path=<path> Indicate a module search path. default is current\n"
  57. " directory('./')\n");
  58. #endif
  59. #if WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0
  60. printf(" --max-threads=n Set maximum thread number per cluster, default is 4\n");
  61. #endif
  62. #if WASM_ENABLE_DEBUG_INTERP != 0
  63. printf(" -g=ip:port Set the debug sever address, default is debug disabled\n");
  64. printf(" if port is 0, then a random port will be used\n");
  65. #endif
  66. printf(" --version Show version information\n");
  67. return 1;
  68. }
  69. /* clang-format on */
  70. static const void *
  71. app_instance_main(wasm_module_inst_t module_inst)
  72. {
  73. const char *exception;
  74. wasm_application_execute_main(module_inst, app_argc, app_argv);
  75. exception = wasm_runtime_get_exception(module_inst);
  76. return exception;
  77. }
  78. static const void *
  79. app_instance_func(wasm_module_inst_t module_inst, const char *func_name)
  80. {
  81. wasm_application_execute_func(module_inst, func_name, app_argc - 1,
  82. app_argv + 1);
  83. /* The result of wasm function or exception info was output inside
  84. wasm_application_execute_func(), here we don't output them again. */
  85. return wasm_runtime_get_exception(module_inst);
  86. }
  87. /**
  88. * Split a space separated strings into an array of strings
  89. * Returns NULL on failure
  90. * Memory must be freed by caller
  91. * Based on: http://stackoverflow.com/a/11198630/471795
  92. */
  93. static char **
  94. split_string(char *str, int *count)
  95. {
  96. char **res = NULL, **res1;
  97. char *p, *next_token;
  98. int idx = 0;
  99. /* split string and append tokens to 'res' */
  100. do {
  101. p = strtok_s(str, " ", &next_token);
  102. str = NULL;
  103. res1 = res;
  104. res = (char **)realloc(res1, sizeof(char *) * (uint32)(idx + 1));
  105. if (res == NULL) {
  106. free(res1);
  107. return NULL;
  108. }
  109. res[idx++] = p;
  110. } while (p);
  111. /**
  112. * Due to the function name,
  113. * res[0] might contain a '\' to indicate a space
  114. * func\name -> func name
  115. */
  116. p = strchr(res[0], '\\');
  117. while (p) {
  118. *p = ' ';
  119. p = strchr(p, '\\');
  120. }
  121. if (count) {
  122. *count = idx - 1;
  123. }
  124. return res;
  125. }
  126. static void *
  127. app_instance_repl(wasm_module_inst_t module_inst)
  128. {
  129. char buffer[4096];
  130. char *cmd;
  131. size_t n;
  132. while ((printf("webassembly> "), fflush(stdout),
  133. cmd = fgets(buffer, sizeof(buffer), stdin))
  134. != NULL) {
  135. bh_assert(cmd);
  136. n = strlen(cmd);
  137. if (cmd[n - 1] == '\n') {
  138. if (n == 1)
  139. continue;
  140. else
  141. cmd[n - 1] = '\0';
  142. }
  143. if (!strcmp(cmd, "__exit__")) {
  144. printf("exit repl mode\n");
  145. break;
  146. }
  147. app_argv = split_string(cmd, &app_argc);
  148. if (app_argv == NULL) {
  149. LOG_ERROR("Wasm prepare param failed: split string failed.\n");
  150. break;
  151. }
  152. if (app_argc != 0) {
  153. const char *exception;
  154. wasm_application_execute_func(module_inst, app_argv[0],
  155. app_argc - 1, app_argv + 1);
  156. if ((exception = wasm_runtime_get_exception(module_inst)))
  157. printf("%s\n", exception);
  158. }
  159. free(app_argv);
  160. }
  161. return NULL;
  162. }
  163. #if WASM_ENABLE_GLOBAL_HEAP_POOL != 0
  164. static char global_heap_buf[WASM_GLOBAL_HEAP_SIZE] = { 0 };
  165. #else
  166. static void *
  167. malloc_func(
  168. #if WASM_MEM_ALLOC_WITH_USER_DATA != 0
  169. void *user_data,
  170. #endif
  171. unsigned int size)
  172. {
  173. return malloc(size);
  174. }
  175. static void *
  176. realloc_func(
  177. #if WASM_MEM_ALLOC_WITH_USER_DATA != 0
  178. void *user_data,
  179. #endif
  180. void *ptr, unsigned int size)
  181. {
  182. return realloc(ptr, size);
  183. }
  184. static void
  185. free_func(
  186. #if WASM_MEM_ALLOC_WITH_USER_DATA != 0
  187. void *user_data,
  188. #endif
  189. void *ptr)
  190. {
  191. free(ptr);
  192. }
  193. #endif /* end of WASM_ENABLE_GLOBAL_HEAP_POOL */
  194. #if WASM_ENABLE_MULTI_MODULE != 0
  195. static char *
  196. handle_module_path(const char *module_path)
  197. {
  198. /* next character after '=' */
  199. return (strchr(module_path, '=')) + 1;
  200. }
  201. static char *module_search_path = ".";
  202. static bool
  203. module_reader_callback(package_type_t module_type, const char *module_name,
  204. uint8 **p_buffer, uint32 *p_size)
  205. {
  206. char *file_format = NULL;
  207. #if WASM_ENABLE_INTERP != 0
  208. if (module_type == Wasm_Module_Bytecode)
  209. file_format = ".wasm";
  210. #endif
  211. #if WASM_ENABLE_AOT != 0
  212. if (module_type == Wasm_Module_AoT)
  213. file_format = ".aot";
  214. #endif
  215. bh_assert(file_format);
  216. const char *format = "%s/%s%s";
  217. int sz = strlen(module_search_path) + strlen("/") + strlen(module_name)
  218. + strlen(file_format) + 1;
  219. char *wasm_file_name = wasm_runtime_malloc(sz);
  220. if (!wasm_file_name) {
  221. return false;
  222. }
  223. snprintf(wasm_file_name, sz, format, module_search_path, module_name,
  224. file_format);
  225. *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_name, p_size);
  226. wasm_runtime_free(wasm_file_name);
  227. return *p_buffer != NULL;
  228. }
  229. static void
  230. module_destroyer_callback(uint8 *buffer, uint32 size)
  231. {
  232. if (!buffer) {
  233. return;
  234. }
  235. wasm_runtime_free(buffer);
  236. buffer = NULL;
  237. }
  238. #endif /* WASM_ENABLE_MULTI_MODULE */
  239. int
  240. main(int argc, char *argv[])
  241. {
  242. int32 ret = -1;
  243. char *wasm_file = NULL;
  244. const char *func_name = NULL;
  245. uint8 *wasm_file_buf = NULL;
  246. uint32 wasm_file_size;
  247. uint32 stack_size = 64 * 1024;
  248. #if WASM_ENABLE_LIBC_WASI != 0
  249. uint32 heap_size = 0;
  250. #else
  251. uint32 heap_size = 16 * 1024;
  252. #endif
  253. #if WASM_ENABLE_GC != 0
  254. uint32 gc_heap_size = GC_HEAP_SIZE_DEFAULT;
  255. #endif
  256. #if WASM_ENABLE_JIT != 0
  257. uint32 llvm_jit_size_level = 3;
  258. uint32 llvm_jit_opt_level = 3;
  259. #endif
  260. wasm_module_t wasm_module = NULL;
  261. wasm_module_inst_t wasm_module_inst = NULL;
  262. RunningMode running_mode = 0;
  263. RuntimeInitArgs init_args;
  264. char error_buf[128] = { 0 };
  265. #if WASM_ENABLE_LOG != 0
  266. int log_verbose_level = 2;
  267. #endif
  268. bool is_repl_mode = false;
  269. bool is_xip_file = false;
  270. #if WASM_ENABLE_LIBC_WASI != 0
  271. libc_wasi_parse_context_t wasi_parse_ctx;
  272. #endif
  273. #if WASM_ENABLE_DEBUG_INTERP != 0
  274. char *ip_addr = NULL;
  275. int instance_port = 0;
  276. #endif
  277. #if WASM_ENABLE_LIBC_WASI != 0
  278. memset(&wasi_parse_ctx, 0, sizeof(wasi_parse_ctx));
  279. #endif
  280. /* Process options. */
  281. for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
  282. if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--function")) {
  283. argc--, argv++;
  284. if (argc < 2) {
  285. return print_help();
  286. }
  287. func_name = argv[0];
  288. }
  289. #if WASM_ENABLE_INTERP != 0
  290. else if (!strcmp(argv[0], "--interp")) {
  291. running_mode = Mode_Interp;
  292. }
  293. #endif
  294. #if WASM_ENABLE_FAST_JIT != 0
  295. else if (!strcmp(argv[0], "--fast-jit")) {
  296. running_mode = Mode_Fast_JIT;
  297. }
  298. #endif
  299. #if WASM_ENABLE_JIT != 0
  300. else if (!strcmp(argv[0], "--llvm-jit")) {
  301. running_mode = Mode_LLVM_JIT;
  302. }
  303. #endif
  304. #if WASM_ENABLE_JIT != 0 && WASM_ENABLE_FAST_JIT != 0
  305. else if (!strcmp(argv[0], "--multi-tier-jit")) {
  306. running_mode = Mode_Multi_Tier_JIT;
  307. }
  308. #endif
  309. #if WASM_ENABLE_LOG != 0
  310. else if (!strncmp(argv[0], "-v=", 3)) {
  311. log_verbose_level = atoi(argv[0] + 3);
  312. if (log_verbose_level < 0 || log_verbose_level > 5)
  313. return print_help();
  314. }
  315. #endif
  316. else if (!strcmp(argv[0], "--repl")) {
  317. is_repl_mode = true;
  318. }
  319. else if (!strncmp(argv[0], "--stack-size=", 13)) {
  320. if (argv[0][13] == '\0')
  321. return print_help();
  322. stack_size = atoi(argv[0] + 13);
  323. }
  324. else if (!strncmp(argv[0], "--heap-size=", 12)) {
  325. if (argv[0][12] == '\0')
  326. return print_help();
  327. heap_size = atoi(argv[0] + 12);
  328. }
  329. #if WASM_ENABLE_GC != 0
  330. else if (!strncmp(argv[0], "--gc-heap-size=", 15)) {
  331. if (argv[0][15] == '\0')
  332. return print_help();
  333. gc_heap_size = atoi(argv[0] + 15);
  334. }
  335. #endif
  336. #if WASM_ENABLE_JIT != 0
  337. else if (!strncmp(argv[0], "--llvm-jit-size-level=", 22)) {
  338. if (argv[0][22] == '\0')
  339. return print_help();
  340. llvm_jit_size_level = atoi(argv[0] + 22);
  341. if (llvm_jit_size_level < 1) {
  342. printf("LLVM JIT size level shouldn't be smaller than 1, "
  343. "setting it to 1\n");
  344. llvm_jit_size_level = 1;
  345. }
  346. else if (llvm_jit_size_level > 3) {
  347. printf("LLVM JIT size level shouldn't be greater than 3, "
  348. "setting it to 3\n");
  349. llvm_jit_size_level = 3;
  350. }
  351. }
  352. else if (!strncmp(argv[0], "--llvm-jit-opt-level=", 21)) {
  353. if (argv[0][21] == '\0')
  354. return print_help();
  355. llvm_jit_opt_level = atoi(argv[0] + 21);
  356. if (llvm_jit_opt_level < 1) {
  357. printf("LLVM JIT opt level shouldn't be smaller than 1, "
  358. "setting it to 1\n");
  359. llvm_jit_opt_level = 1;
  360. }
  361. else if (llvm_jit_opt_level > 3) {
  362. printf("LLVM JIT opt level shouldn't be greater than 3, "
  363. "setting it to 3\n");
  364. llvm_jit_opt_level = 3;
  365. }
  366. }
  367. #endif
  368. #if WASM_ENABLE_MULTI_MODULE != 0
  369. else if (!strncmp(argv[0], MODULE_PATH, strlen(MODULE_PATH))) {
  370. module_search_path = handle_module_path(argv[0]);
  371. if (!strlen(module_search_path)) {
  372. return print_help();
  373. }
  374. }
  375. #endif
  376. #if WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0
  377. else if (!strncmp(argv[0], "--max-threads=", 14)) {
  378. if (argv[0][14] == '\0')
  379. return print_help();
  380. wasm_runtime_set_max_thread_num(atoi(argv[0] + 14));
  381. }
  382. #endif
  383. #if WASM_ENABLE_DEBUG_INTERP != 0
  384. else if (!strncmp(argv[0], "-g=", 3)) {
  385. char *port_str = strchr(argv[0] + 3, ':');
  386. char *port_end;
  387. if (port_str == NULL)
  388. return print_help();
  389. *port_str = '\0';
  390. instance_port = strtoul(port_str + 1, &port_end, 10);
  391. if (port_str[1] == '\0' || *port_end != '\0')
  392. return print_help();
  393. ip_addr = argv[0] + 3;
  394. }
  395. #endif
  396. else if (!strcmp(argv[0], "--version")) {
  397. uint32 major, minor, patch;
  398. wasm_runtime_get_version(&major, &minor, &patch);
  399. printf("iwasm %" PRIu32 ".%" PRIu32 ".%" PRIu32 "\n", major, minor,
  400. patch);
  401. return 0;
  402. }
  403. else {
  404. #if WASM_ENABLE_LIBC_WASI != 0
  405. libc_wasi_parse_result_t result =
  406. libc_wasi_parse(argv[0], &wasi_parse_ctx);
  407. switch (result) {
  408. case LIBC_WASI_PARSE_RESULT_OK:
  409. continue;
  410. case LIBC_WASI_PARSE_RESULT_NEED_HELP:
  411. return print_help();
  412. case LIBC_WASI_PARSE_RESULT_BAD_PARAM:
  413. return 1;
  414. }
  415. #else
  416. return print_help();
  417. #endif
  418. }
  419. }
  420. if (argc == 0)
  421. return print_help();
  422. wasm_file = argv[0];
  423. app_argc = argc;
  424. app_argv = argv;
  425. memset(&init_args, 0, sizeof(RuntimeInitArgs));
  426. init_args.running_mode = running_mode;
  427. #if WASM_ENABLE_GLOBAL_HEAP_POOL != 0
  428. init_args.mem_alloc_type = Alloc_With_Pool;
  429. init_args.mem_alloc_option.pool.heap_buf = global_heap_buf;
  430. init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf);
  431. #else
  432. init_args.mem_alloc_type = Alloc_With_Allocator;
  433. #if WASM_MEM_ALLOC_WITH_USER_DATA != 0
  434. /* Set user data for the allocator is needed */
  435. /* init_args.mem_alloc_option.allocator.user_data = user_data; */
  436. #endif
  437. init_args.mem_alloc_option.allocator.malloc_func = malloc_func;
  438. init_args.mem_alloc_option.allocator.realloc_func = realloc_func;
  439. init_args.mem_alloc_option.allocator.free_func = free_func;
  440. #endif
  441. #if WASM_ENABLE_GC != 0
  442. init_args.gc_heap_size = gc_heap_size;
  443. #endif
  444. #if WASM_ENABLE_JIT != 0
  445. init_args.llvm_jit_size_level = llvm_jit_size_level;
  446. init_args.llvm_jit_opt_level = llvm_jit_opt_level;
  447. #endif
  448. #if WASM_ENABLE_DEBUG_INTERP != 0
  449. init_args.instance_port = instance_port;
  450. if (ip_addr)
  451. /* ensure that init_args.ip_addr is null terminated */
  452. strncpy_s(init_args.ip_addr, sizeof(init_args.ip_addr) - 1, ip_addr,
  453. strlen(ip_addr));
  454. #endif
  455. /* initialize runtime environment */
  456. if (!wasm_runtime_full_init(&init_args)) {
  457. printf("Init runtime environment failed.\n");
  458. return -1;
  459. }
  460. #if WASM_ENABLE_LOG != 0
  461. bh_log_set_verbose_level(log_verbose_level);
  462. #endif
  463. /* load WASM byte buffer from WASM bin file */
  464. if (!(wasm_file_buf =
  465. (uint8 *)bh_read_file_to_buffer(wasm_file, &wasm_file_size)))
  466. goto fail1;
  467. #if WASM_ENABLE_AOT != 0
  468. if (wasm_runtime_is_xip_file(wasm_file_buf, wasm_file_size)) {
  469. uint8 *wasm_file_mapped;
  470. int map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC;
  471. int map_flags = MMAP_MAP_32BIT;
  472. if (!(wasm_file_mapped = os_mmap(NULL, (uint32)wasm_file_size, map_prot,
  473. map_flags, os_get_invalid_handle()))) {
  474. printf("mmap memory failed\n");
  475. wasm_runtime_free(wasm_file_buf);
  476. goto fail1;
  477. }
  478. bh_memcpy_s(wasm_file_mapped, wasm_file_size, wasm_file_buf,
  479. wasm_file_size);
  480. wasm_runtime_free(wasm_file_buf);
  481. wasm_file_buf = wasm_file_mapped;
  482. is_xip_file = true;
  483. }
  484. #endif
  485. #if WASM_ENABLE_MULTI_MODULE != 0
  486. wasm_runtime_set_module_reader(module_reader_callback,
  487. module_destroyer_callback);
  488. #endif
  489. /* load WASM module */
  490. if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size,
  491. error_buf, sizeof(error_buf)))) {
  492. printf("%s\n", error_buf);
  493. goto fail2;
  494. }
  495. #if WASM_ENABLE_LIBC_WASI != 0
  496. libc_wasi_init(wasm_module, argc, argv, &wasi_parse_ctx);
  497. #endif
  498. /* instantiate the module */
  499. if (!(wasm_module_inst =
  500. wasm_runtime_instantiate(wasm_module, stack_size, heap_size,
  501. error_buf, sizeof(error_buf)))) {
  502. printf("%s\n", error_buf);
  503. goto fail3;
  504. }
  505. #if WASM_ENABLE_DEBUG_INTERP != 0
  506. if (ip_addr != NULL) {
  507. wasm_exec_env_t exec_env =
  508. wasm_runtime_get_exec_env_singleton(wasm_module_inst);
  509. uint32_t debug_port;
  510. if (exec_env == NULL) {
  511. printf("%s\n", wasm_runtime_get_exception(wasm_module_inst));
  512. goto fail4;
  513. }
  514. debug_port = wasm_runtime_start_debug_instance(exec_env);
  515. if (debug_port == 0) {
  516. printf("Failed to start debug instance\n");
  517. goto fail4;
  518. }
  519. }
  520. #endif
  521. ret = 0;
  522. const char *exception = NULL;
  523. if (is_repl_mode) {
  524. app_instance_repl(wasm_module_inst);
  525. }
  526. else if (func_name) {
  527. exception = app_instance_func(wasm_module_inst, func_name);
  528. if (exception) {
  529. /* got an exception */
  530. ret = 1;
  531. }
  532. }
  533. else {
  534. exception = app_instance_main(wasm_module_inst);
  535. if (exception) {
  536. /* got an exception */
  537. ret = 1;
  538. }
  539. }
  540. #if WASM_ENABLE_LIBC_WASI != 0
  541. if (ret == 0) {
  542. /* propagate wasi exit code. */
  543. ret = wasm_runtime_get_wasi_exit_code(wasm_module_inst);
  544. }
  545. #endif
  546. if (exception)
  547. printf("%s\n", exception);
  548. #if WASM_ENABLE_DEBUG_INTERP != 0
  549. fail4:
  550. #endif
  551. /* destroy the module instance */
  552. wasm_runtime_deinstantiate(wasm_module_inst);
  553. fail3:
  554. /* unload the module */
  555. wasm_runtime_unload(wasm_module);
  556. fail2:
  557. /* free the file buffer */
  558. if (!is_xip_file)
  559. wasm_runtime_free(wasm_file_buf);
  560. else
  561. os_munmap(wasm_file_buf, wasm_file_size);
  562. fail1:
  563. /* destroy runtime environment */
  564. wasm_runtime_destroy();
  565. return ret;
  566. }