Stream: git-wasmtime

Topic: wasmtime / issue #9922 Error: failed to invoke command de...


view this post on Zulip Wasmtime GitHub notifications bot (Jan 05 2025 at 19:00):

guest271314 added the bug label to Issue #9922.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 05 2025 at 19:00):

guest271314 opened issue #9922:

Thanks for filing a bug report! Please fill out the TODOs below.

Note: if you want to report a security issue, please read our security policy!

Test Case

hermes-builds-fopen.tar.gz

Steps to Reproduce

Follow instructions here to build Facebook's Static Hermes.

Create fopen.wasm from fopen.ts.

#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

set -e  # Exit immediately if a command exits with a non-zero status
set -u  # Treat unset variables as an error and exit immediately

# Extract the filename without path and extension
file_name=$(basename "$1")   # Remove path
file_name_with_ext="${file_name}"
file_name="${file_name%.*}"      # Remove extension

rm -rf out

mkdir out

out="$PWD/out"

# rm -rf ${file_name}.c ${file_name}.o ${file_name}.wasm ${file_name}.hbc ${file_name}

# ./build-host/bin/hermes ${file_name}.js --emit-binary -out "${out}/${file_name}.hbc"

./build-host/bin/shermes -v -Os -g -static-link -typed -fstatic-builtins -Xenable-tdz -emit-c "${file_name_with_ext}" -o "${out}/${file_name}.c"

# ./build-host/bin/shermes -v -Os -g -Xenable-tdz \
# -fstatic-builtins -fstd-globals ${file_name}.js \
# -o "${out}/${file_name}"
# cp fopen.c out/fopen.c

../wasi-sdk/bin/wasm32-wasi-clang "${out}/${file_name}.c" -c \
  -O3 \
  -DNDEBUG \
  -fno-strict-aliasing -fno-strict-overflow \
  -I./build-wasm/lib/config \
  -I../hermes/include \
  -mllvm -wasm-enable-sjlj \
  -Wno-c23-extensions \
  -o "${out}/${file_name}.o"

../wasi-sdk/bin/clang++ -O3 "${out}/${file_name}.o" ./build-wasm/tools/sh-demo/CMakeFiles/sh-demo.dir/cxa.cpp.obj -o "${out}/${file_name}.wasm" \
  -L./build-wasm/lib \
  -L./build-wasm/jsi \
  -L./build-wasm/tools/shermes \
  -lshermes_console_a -lhermesvmlean_a -ljsi -lwasi-emulated-mman -lsetjmp

../wasi-sdk/bin/strip "${out}/${file_name}.wasm"

ls -lh "${out}"
./wasm-standalone-test.sh fopen.ts
In file included from /media/user/1234/hermes-builds/out/fopen.c:2:
../hermes/include/hermes/VM/static_h.h:334:2: warning: "JS exceptions are currenly broken with WASI" [-W#warnings]
  334 | #warning "JS exceptions are currenly broken with WASI"
      |  ^
1 warning generated.
total 1.7M
-rw-rw-r-- 1 user user  96K Jan  5 09:46 fopen.c
-rw-rw-r-- 1 user user  25K Jan  5 09:46 fopen.o
-rwxrwxr-x 1 user user 1.5M Jan  5 09:46 fopen.wasm

Expected Results

wasmer version wasmer 5.0.4 produces the expected result

echo '9 5' |  wasmer out/fopen.wasm
5 of 362879 (0-indexed, factorial 362880) => [0,1,2,3,4,5,8,7,6]
echo '10 4' |  wasmer out/fopen.wasm
4 of 3628799 (0-indexed, factorial 3628800) => [0,1,2,3,4,5,6,9,7,8]

Actual Results

echo '4 5' |  wasmtime out/fopen.wasm
Error: failed to run main module `out/fopen.wasm`

Caused by:
    0: failed to invoke command default
    1: error while executing at wasm backtrace:
           0: 0x130e14 - <unknown>!<wasm function 4066>
           1: 0xa5995 - <unknown>!<wasm function 2511>
           2: 0xa5e29 - <unknown>!<wasm function 2520>
           3: 0x5884 - <unknown>!<wasm function 33>
           4: 0x2be4 - <unknown>!<wasm function 24>
           5: 0x25bd - <unknown>!<wasm function 23>
           6: 0xb9a8c - <unknown>!<wasm function 2934>
           7: 0xb9984 - <unknown>!<wasm function 2933>
           8: 0xb96a2 - <unknown>!<wasm function 2932>
           9: 0xb95c6 - <unknown>!<wasm function 2931>
          10: 0x35ac - <unknown>!<wasm function 25>
          11: 0x130be0 - <unknown>!<wasm function 4044>
          12: 0x2393 - <unknown>!<wasm function 21>
    2: wasm trap: wasm `unreachable` instruction executed

Without using wasi-sdk/bin/strip

echo '4 5' |  WASM_BACKTRACE_DETAILS=1 wasmtime  out/fopen.wasm
Error: failed to run main module `out/fopen.wasm`

Caused by:
    0: failed to invoke command default
    1: error while executing at wasm backtrace:
           0: 0x130e14 - fopen.wasm!abort
           1: 0xa5995 - fopen.wasm!_sh_throw_current
           2: 0xa5e29 - fopen.wasm!_sh_throw
           3: 0x5884 - fopen.wasm!_7_fopen
           4: 0x2be4 - fopen.wasm!_1__1_
           5: 0x25bd - fopen.wasm!_0_global
           6: 0xb9a8c - fopen.wasm!sh_unit_run(SHRuntime*, SHUnit*)
           7: 0xb9984 - fopen.wasm!_sh_unit_init
           8: 0xb96a2 - fopen.wasm!_sh_unit_init_guarded
           9: 0xb95c6 - fopen.wasm!_sh_initialize_units
          10: 0x35ac - fopen.wasm!main
          11: 0x130be0 - fopen.wasm!__main_void
          12: 0x2393 - fopen.wasm!_start
       note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information
    2: wasm trap: wasm `unreachable` instruction executed

Versions and Environment

Wasmtime version or commit: wasmtime 28.0.0 (2e584e841 2024-12-20)

Operating system: Linux

Architecture: x86_64 x86_64 x86_64 GNU/Linux

Extra Info

I initially thought this was an issue with Static Hermes source code, specifically related to reading STDIN. Then I tested the same WASM file with wasmer.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 05 2025 at 19:35):

bjorn3 commented on issue #9922:

WASI doesn't have /dev nor /dev/stdin. Looks like Wasmer adds /dev/stdin as non-standard extension: https://github.com/wasmerio/wasmer/blob/1d03406174b204ac403f61e2deb0a41601c8e41b/lib/virtual-fs/src/builder.rs#L106-L110 Instead of using fopen("/dev/stdin") you can use the stdin global variable instead. This is of type FILE * just like the return value of fopen. You just have to make sure not to fclose it.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 05 2025 at 20:37):

guest271314 commented on issue #9922:

stdin is not globally defined in the source JavaScript.

That's the only way I know of to read STDIN in Static Hermes. Modified from this https://github.com/facebook/hermes/blob/static_h/examples/ffi/fopen.js.

fopen.ts

/**

 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *

 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

// https://github.com/facebook/hermes/tree/static_h/examples/ffi
"use strict";

// Emulate a module scope, since global scope is unsound.
(function (exports) {
  const c_null = $SHBuiltin.c_null();

  // stdio.h
  const _fopen = $SHBuiltin.extern_c(
    { include: "stdio.h" },
    function fopen(path: c_ptr, mode: c_ptr): c_ptr {
      throw 0;
    },
  );
  const _fclose = $SHBuiltin.extern_c(
    { include: "stdio.h" },
    function fclose(f: c_ptr): void {
    },
  );
  const _fread = $SHBuiltin.extern_c(
    { include: "stdio.h" },
    function fread(
      ptr: c_ptr,
      size: c_size_t,
      nitems: c_size_t,
      stream: c_ptr,
    ): c_size_t {
      throw 0;
    },
  );

  // stdlib.h
  const _malloc = $SHBuiltin.extern_c(
    { include: "stdlib.h" },
    function malloc(size: c_size_t): c_ptr {
      throw 0;
    },
  );
  const _free = $SHBuiltin.extern_c(
    { include: "stdlib.h" },
    function free(p: c_ptr): void {
    },
  );

  // string.h
  const _strerror_r = $SHBuiltin.extern_c(
    { include: "string.h" },
    function strerror_r(errnum: c_int, errbuf: c_ptr, buflen: c_size_t): c_int {
      throw 0;
    },
  );

  // Builtin provided by SH to wrap errno.
  const _sh_errno = $SHBuiltin.extern_c(
    { declared: true },
    function _sh_errno(): c_int {
      throw 0;
    },
  );

  // Pointer access builtins.
  const _ptr_write_char = $SHBuiltin.extern_c(
    { declared: true },
    function _sh_ptr_write_char(ptr: c_ptr, offset: c_int, v: c_char): void {
    },
  );
  const _ptr_read_uchar = $SHBuiltin.extern_c(
    { declared: true },
    function _sh_ptr_read_uchar(ptr: c_ptr, offset: c_int): c_uchar {
      throw 0;
    },
  );

  /// Allocate native memory using malloc() or throw an exception.
  function malloc(size: number): c_ptr {
    "inline";
    "use unsafe";

    let res = _malloc(size);
    if (res === 0) throw Error("OOM");
    return res;
  }

  /// Convert a JS string to ASCIIZ.
  function stringToAsciiz(s: any): c_ptr {
    "use unsafe";

    if (typeof s !== "string") s = String(s);
    let buf = malloc(s.length + 1);
    try {
      let i = 0;
      for (let e = s.length; i < e; ++i) {
        let code: number = s.charCodeAt(i);
        if (code > 127) throw Error("String is not ASCII");
        _ptr_write_char(buf, i, code);
      }
      _ptr_write_char(buf, i, 0);
      return buf;
    } catch (e) {
      _free(buf);
      throw e;
    }
  }

  /// Convert an ASCII string of certain size to a JS string.
  function asciiToString_unsafe(buf: c_ptr, size: number): string {
    let res = "";
    for (let i = 0; i < size; ++i) {
      let ch = _ptr_read_uchar(buf, i);
      // if (ch > 127) throw Error("String is not ASCII");
      res += String.fromCharCode(ch);
    }
    return res;
  }

  /// Convert an ASCIIZ string up to a maximum size to a JS string.
  function asciizToString_unsafe(buf: c_ptr, maxsize: number): string {
    let res = "";
    for (let i = 0; i < maxsize; ++i) {
      let ch = _ptr_read_uchar(buf, i);
      //if (ch > 127) throw Error("String is not ASCII");
      //if (ch === 0) break;
      res += String.fromCharCode(ch);
    }
    return res;
  }

  function strerror(errnum: number): string {
    "use unsafe";

    let errbuf = malloc(1024);
    try {
      _strerror_r(errnum, errbuf, 1024);
      return asciizToString_unsafe(errbuf, 1024);
    } finally {
      _free(errbuf);
    }
  }

  /// Very simple hack to ensure safety.
  let handles: c_ptr[] = [];
  // FIXME: fast array doesn't support .pop() yet.
  let closedHandles = Array();

  function fopen(path: string, mode: string): number {
    "use unsafe";

    let pathz: c_ptr = c_null;
    let modez: c_ptr = c_null;
    try {
      pathz = stringToAsciiz(path);
      modez = stringToAsciiz(mode);
      let filePtr = _fopen(pathz, modez);
      if (!filePtr) {
        let errnum = _sh_errno();
        throw Error(path + ": " + strerror(errnum));
      }
      // Allocate a handle.
      if (closedHandles.length > 0) {
        let f = closedHandles.pop();
        handles[f] = filePtr;
        return f;
      }
      handles.push(filePtr);
      return handles.length - 1;
    } finally {
      _free(pathz);
      _free(modez);
    }
  }

  function fclose(f: number): void {
    "use unsafe";

    if (f < 0 || f >= handles.length) throw Error("invalid file handle");
    if (handles[f]) {
      _fclose(handles[f]);
      handles[f] = c_null;
      closedHandles.push(f);
    }
  }

  function fread(size: number, f: number): string {
    "use unsafe";

    if (f < 0 || f >= handles.length) throw Error("invalid file handle");
    if (!handles[f]) throw Error("file is closed");

    if (size <= 0) throw Error("invalid size");
    let buf = malloc(size);
    try {
      let nr = _fread(buf, 1, size, handles[f]);
      return asciiToString_unsafe(buf, nr);
    } finally {
      _free(buf);
    }
  }

  function freadAll(f: number): string {
    let res = "";
    for (;;) {
      let s = fread(1024, f);
      if (!s) break;
      res += s;
    }
    return res;
  }

  // https://stackoverflow.com/a/34238979

  function array_nth_permutation(a, n) {
    let lex = n;
    let b = Array(); // copy of the set a.slice()
    for (let x = 0; x < a.length; x++) {
      b[x] = a[x];
    }
    let len = a.length; // length of the set
    const res = Array(); // return value, undefined
    let i = 1;
    let f = 1;

    // compute f = factorial(len)
    for (; i <= len; i++) {
      f *= i;
    }

    let fac = f;
    // if the permutation number is within range
    if (n >= 0 && n < f) {
      // start with the empty set, loop for len elements
      // let result_len = 0;
      for (; len > 0; len--) {
        // determine the next element:
        // there are f/len subsets for each possible element,
        f /= len;
        // a simple division gives the leading element index
        i = (n - n % f) / f; // Math.floor(n / f);
        // alternately: i = (n - n % f) / f;
        // res[(result_len)++] = b[i];
        // for (let j = i; j < len; j++) {
        //   b[j] = b[j + 1]; // shift elements left
        // }
        res.push(b.splice(i, 1)[0]);
        // reduce n for the remaining subset:
        // compute the remainder of the above division
        n %= f;
        // extract the i-th element from b and push it at the end of res
      }
      return `${lex} of ${fac - 1} (0-indexed, factorial ${fac}) => ${
        JSON.stringify(res)
      }`;
    } else {
      if (n === 0) {
        return `${JSON.stringify(res)}\n`;
      }
      return `${n} >= 0 && ${n} < ${f}: ${n >= 0 && n < f}`;
    }
  }

  let f = fopen("/dev/stdin", "r");
  try {
    let [input, lex] = String(freadAll(f)).split(" ").map(Number);
    if (input < 2 || lex < 0) {
      print(`Expected n > 2, m >= 0, got ${input}, ${lex}\n`); // eval(input)
      return 1;
    } else {
      print(
        array_nth_permutation(Array.from({ length: input }, (_, i) => i), lex),
      );
      return 0;
    }
  } finally {
    fclose(f);
  }

  // Optionally force some methods to be emitted for debugging.
  // exports.foo = foo;
})({});

Substituting "stdin" for "/dev/stdin" produces the same result.

Trying to maunally change the contents of the compiled C file produces other errors

fopen.ts:158:39: error: incompatible pointer to integer conversion passing 'FILE *const' (aka 'struct _IO_FILE *const') to parameter of type 'SHSymbolID' (aka 'unsigned int') [-Wint-conversion]
  158 |   locals.t9 = _sh_ljs_get_string(shr, stdin /*/dev/stdin*/);
/wasi-sdk/bin/../share/wasi-sysroot/include/wasm32-wasi/stdio.h:79:16: note: expanded from macro 'stdin'
   79 | #define stdin  (stdin)
      |                ^~~~~~~
../hermes/include/hermes/VM/static_h.h:653:47: note: passing argument to parameter 'symID' here
  653 | _sh_ljs_get_string(SHRuntime *shr, SHSymbolID symID);
      |                                               ^
1 warning and 1 error generated.
clang++: error: no such file or directory: 'fopen.o'

Would using a similar mapping as wasmer does work in `wasmtime?

view this post on Zulip Wasmtime GitHub notifications bot (Jan 05 2025 at 21:25):

cfallin commented on issue #9922:

Would using a similar mapping as wasmer does work in wasmtime?

In general, no, we try not to make non-standard extensions to WASI. This has negative downstream consequences on the ecosystem -- it creates expectations of other engines and a back-and-forth of bug reports (exactly as we see here!) that ends up forcing a de-facto standard, without the usual discussion and care, which we want to avoid.

If Static Hermes can only access stdin by opening a file named /dev/stdin, that seems like an issue with their standard library on WASI -- perhaps you could report an issue upstream?

view this post on Zulip Wasmtime GitHub notifications bot (Jan 05 2025 at 21:37):

guest271314 commented on issue #9922:

@cfallin I think I wore out my welcome there to get this far How to translate gcc CLI arguments to correponding clang arguments? #1588. A Collaborator over there has taken the time to implement compiling JavaScript to WASM using Emscripten and JavaScript to WASM with WASI support over a couple weeks. With patience until the last comment.

Technically Static Hermes doesn't provide a way at all to read STDIN, officially.

I'm basically trying to reproduce what is possible with Bytecode Alliance's Javy, with the bonus of the capability to compile to native executable, too, with the same C source code output by -emit-c. So far WABT's wasm2c has not yet implemented outputting a main.c for wasm2c nm_javy_permutations.wasm. With shermes we get C first, then we can branch to WASM standalone and WASM with WASI support, and nativ executable; and if we want, back to JavaScript with wasm2js.

I had no idea wasmer was doing something different that is not standardized. I know from my experiments in WebAssembly world that the Wasmer folks to their own thing.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 05 2025 at 21:38):

bjorn3 commented on issue #9922:

stdin is not globally defined in the source JavaScript.

I meant importing libc's stdin global variable.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 05 2025 at 21:43):

guest271314 commented on issue #9922:

Havn't tried that, but thought about it. I'm not sure how to do that given the base examples/fopen.js. I don't think Facebook' Hermes is interested in implementing reading STDIN.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 05 2025 at 22:07):

guest271314 commented on issue #9922:

@bjorn3

  const _stdin = $SHBuiltin.extern_c(
    { include: "stdio.h" },
    function (): c_ptr {
      return /* ? */
    }
  );

?

view this post on Zulip Wasmtime GitHub notifications bot (Jan 05 2025 at 22:31):

bjorn3 commented on issue #9922:

I have no clue how to import C global variables from js code with static hermes. I hadn't heard of static hermes before today.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 05 2025 at 22:54):

guest271314 closed issue #9922:

Thanks for filing a bug report! Please fill out the TODOs below.

Note: if you want to report a security issue, please read our security policy!

Test Case

hermes-builds-fopen.tar.gz

Steps to Reproduce

Follow instructions here to build Facebook's Static Hermes.

Create fopen.wasm from fopen.ts.

#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

set -e  # Exit immediately if a command exits with a non-zero status
set -u  # Treat unset variables as an error and exit immediately

# Extract the filename without path and extension
file_name=$(basename "$1")   # Remove path
file_name_with_ext="${file_name}"
file_name="${file_name%.*}"      # Remove extension

rm -rf out

mkdir out

out="$PWD/out"

# rm -rf ${file_name}.c ${file_name}.o ${file_name}.wasm ${file_name}.hbc ${file_name}

# ./build-host/bin/hermes ${file_name}.js --emit-binary -out "${out}/${file_name}.hbc"

./build-host/bin/shermes -v -Os -g -static-link -typed -fstatic-builtins -Xenable-tdz -emit-c "${file_name_with_ext}" -o "${out}/${file_name}.c"

# ./build-host/bin/shermes -v -Os -g -Xenable-tdz \
# -fstatic-builtins -fstd-globals ${file_name}.js \
# -o "${out}/${file_name}"
# cp fopen.c out/fopen.c

../wasi-sdk/bin/wasm32-wasi-clang "${out}/${file_name}.c" -c \
  -O3 \
  -DNDEBUG \
  -fno-strict-aliasing -fno-strict-overflow \
  -I./build-wasm/lib/config \
  -I../hermes/include \
  -mllvm -wasm-enable-sjlj \
  -Wno-c23-extensions \
  -o "${out}/${file_name}.o"

../wasi-sdk/bin/clang++ -O3 "${out}/${file_name}.o" ./build-wasm/tools/sh-demo/CMakeFiles/sh-demo.dir/cxa.cpp.obj -o "${out}/${file_name}.wasm" \
  -L./build-wasm/lib \
  -L./build-wasm/jsi \
  -L./build-wasm/tools/shermes \
  -lshermes_console_a -lhermesvmlean_a -ljsi -lwasi-emulated-mman -lsetjmp

../wasi-sdk/bin/strip "${out}/${file_name}.wasm"

ls -lh "${out}"
./wasm-standalone-test.sh fopen.ts
In file included from /media/user/1234/hermes-builds/out/fopen.c:2:
../hermes/include/hermes/VM/static_h.h:334:2: warning: "JS exceptions are currenly broken with WASI" [-W#warnings]
  334 | #warning "JS exceptions are currenly broken with WASI"
      |  ^
1 warning generated.
total 1.7M
-rw-rw-r-- 1 user user  96K Jan  5 09:46 fopen.c
-rw-rw-r-- 1 user user  25K Jan  5 09:46 fopen.o
-rwxrwxr-x 1 user user 1.5M Jan  5 09:46 fopen.wasm

Expected Results

wasmer version wasmer 5.0.4 produces the expected result

echo '9 5' |  wasmer out/fopen.wasm
5 of 362879 (0-indexed, factorial 362880) => [0,1,2,3,4,5,8,7,6]
echo '10 4' |  wasmer out/fopen.wasm
4 of 3628799 (0-indexed, factorial 3628800) => [0,1,2,3,4,5,6,9,7,8]

Actual Results

echo '4 5' |  wasmtime out/fopen.wasm
Error: failed to run main module `out/fopen.wasm`

Caused by:
    0: failed to invoke command default
    1: error while executing at wasm backtrace:
           0: 0x130e14 - <unknown>!<wasm function 4066>
           1: 0xa5995 - <unknown>!<wasm function 2511>
           2: 0xa5e29 - <unknown>!<wasm function 2520>
           3: 0x5884 - <unknown>!<wasm function 33>
           4: 0x2be4 - <unknown>!<wasm function 24>
           5: 0x25bd - <unknown>!<wasm function 23>
           6: 0xb9a8c - <unknown>!<wasm function 2934>
           7: 0xb9984 - <unknown>!<wasm function 2933>
           8: 0xb96a2 - <unknown>!<wasm function 2932>
           9: 0xb95c6 - <unknown>!<wasm function 2931>
          10: 0x35ac - <unknown>!<wasm function 25>
          11: 0x130be0 - <unknown>!<wasm function 4044>
          12: 0x2393 - <unknown>!<wasm function 21>
    2: wasm trap: wasm `unreachable` instruction executed

Without using wasi-sdk/bin/strip

echo '4 5' |  WASM_BACKTRACE_DETAILS=1 wasmtime  out/fopen.wasm
Error: failed to run main module `out/fopen.wasm`

Caused by:
    0: failed to invoke command default
    1: error while executing at wasm backtrace:
           0: 0x130e14 - fopen.wasm!abort
           1: 0xa5995 - fopen.wasm!_sh_throw_current
           2: 0xa5e29 - fopen.wasm!_sh_throw
           3: 0x5884 - fopen.wasm!_7_fopen
           4: 0x2be4 - fopen.wasm!_1__1_
           5: 0x25bd - fopen.wasm!_0_global
           6: 0xb9a8c - fopen.wasm!sh_unit_run(SHRuntime*, SHUnit*)
           7: 0xb9984 - fopen.wasm!_sh_unit_init
           8: 0xb96a2 - fopen.wasm!_sh_unit_init_guarded
           9: 0xb95c6 - fopen.wasm!_sh_initialize_units
          10: 0x35ac - fopen.wasm!main
          11: 0x130be0 - fopen.wasm!__main_void
          12: 0x2393 - fopen.wasm!_start
       note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information
    2: wasm trap: wasm `unreachable` instruction executed

Versions and Environment

Wasmtime version or commit: wasmtime 28.0.0 (2e584e841 2024-12-20)

Operating system: Linux

Architecture: x86_64 x86_64 x86_64 GNU/Linux

Extra Info

I initially thought this was an issue with Static Hermes source code, specifically related to reading STDIN. Then I tested the same WASM file with wasmer.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 05 2025 at 22:54):

guest271314 commented on issue #9922:

Fair enough. Some background Does hermes expose the capability to read standard input and write standard output? #1469.

In pertinent part https://github.com/facebook/hermes/discussions/1469#discussioncomment-10237485

Unfortunately an I/O library is currently out of scope for Hermes, and is technically orthogonal to our work.

Such a library can be implemented, but it would likely be mostly engine-agnostic.

and https://github.com/facebook/hermes/discussions/1469#discussioncomment-10452501

Static Hermes allows calling of any C API and generates native code.

I'm basically trying to use JavaScript as the source code that can be cross-compiled to various targets.

I can do this to an appreciable degree with Bytecode Alliance's Javy. Minus the compiling to native executable with C output part.

// ./javy emit-plugin -o plugin.wasm
// ./javy build -C dynamic -C plugin=plugin.wasm -o nm_javy_permutations.wasm nm_javy_test.js
// wasmtime run --preload javy_quickjs_provider_v3=plugin.wasm javy-permutations.wasm
// ./binaryen/bin/wasm2js javy-permutations.wasm --enable-bulk-memory -o javy-permutations.js
// node --no-warnings node-javy-test.js '4 5'
// 5 of 23 (0-indexed, factorial 24) => [0,3,2,1]
// echo '4 5' | wasmtime run --dir=. --preload javy_quickjs_provider_v3=plugin.wasm nm_javy_permutations.wasm -
// 5 of 23 (0-indexed, factorial 24) => [0,3,2,1]
// echo "4 5" | node --no-warnings node-javy-test.js -
// 5 of 23 (0-indexed, factorial 24) => [0,3,2,1]

I guess this is not a bug in wasmtime.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 05 2025 at 22:55):

cfallin commented on issue #9922:

@cfallin I think I wore out my welcome there to get this far https://github.com/facebook/hermes/discussions/1588#discussioncomment-11731049.

For what it's worth, I agree with tmikov (intentionally not tagging) in that thread. In particular posting long (100+ line) snippets without attempting to reduce or debug the issue at all is not a super-great use of maintainers' time.

Technically Static Hermes doesn't provide a way at all to read STDIN, officially.

Regardless, it seems that your issue is with Static Hermes: you want a piece of functionality, they do not provide it; I would recommend either working with them or working with your own fork. For what it's worth, maintainers tend to respond more positively to "I have debugged the issue and here is a PR that fixes it" sorts of contributions.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 05 2025 at 23:05):

guest271314 commented on issue #9922:

@cfallin

For what it's worth, I agree with tmikov (intentionally not tagging) in that thread. In particular posting long (100+ line) snippets without attempting to reduce or debug the issue at all is not a super-great use of maintainers' time.

Um, I've generally tested and tried to break code a few thousand times before I file issues.

I'm on an island. There's not a whole bunch of developers hacking node, deno, bun, qjs canary versions tjs, llrt, hermes, shermes, Google's d8, SerenityOS's LibJS js, Mozilla's SpiderMonkey js shell, wasmtime, wasmer, running Chromium Dev Channel, and Firefox Nightly, at the same time, trying to write runtime agnostic code that works in each JavaScript/WebAssembly engine and runtime.

I'm testing your gear in the field. At least until something breaks. I don't expect appreciation. I would at least expect a maintainer to ask what I've tried, before assuming I havn't tried anything. But alas, that rarely happens. Folkds are pigeoonholed in their own domains. I at least try to bridge gaps. Oh, well. The best I can do is test, and try to ask questions, until people ban me. I begin my experiments and tests on edges with these mottos in mind, understanding that not all maintainers, hackers, developers, engineers have the same interests. That's fine, different interests make the world go round!

So we need people to have weird new
ideas ... we need more ideas to break it
and make it better ...

Use it. Break it. File bugs. Request features.

von Braun believed in testing. I cannot
emphasize that term enough – test, test,
test. Test to the point it breaks.

Thanks for your time and effort in this matter. Have a great day. Without rancor.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 07 2025 at 07:10):

guest271314 commented on issue #9922:

WASI doesn't have /dev nor /dev/stdin. Looks like Wasmer adds /dev/stdin as non-standard extension: https://github.com/wasmerio/wasmer/blob/1d03406174b204ac403f61e2deb0a41601c8e41b/lib/virtual-fs/src/builder.rs#L106-L110 Instead of using fopen("/dev/stdin") you can use the stdin global variable instead. This is of type FILE * just like the return value of fopen. You just have to make sure not to fclose it.

@bjorn3 Helpful hint. Thanks.

Using int getchar(void); to avoid trying to define stdin from WASI-SDK stdio.h in Static Hermes context.

  const _getchar = $SHBuiltin.extern_c(
    { include: "stdio.h" },
    function getchar(): c_int {
      throw 0;
    },
  );

  function getchars(): string {
    "use unsafe";
    try {
      let n: number = 50;
      let c: string = String();
      while (--n) {
        let int: number = _getchar();
        if (int === -1) {
          break;
        }
        c += String.fromCodePoint(int);
        // print("int:", int);
        // print("string:", c);
      }
      return String(c).trim();
    } catch (e) {
      return String(e.message);
    }
  }

  let stdin = getchars();
echo '15 2' | wasmer run ./out/fopen.wasm
2 of 1307674367999 (0-indexed, factorial 1307674368000) => [0,1,2,3,4,5,6,7,8,9,10,11,13,12,14]

echo '15 2' | wasmtime run ./out/fopen.wasm
2 of 1307674367999 (0-indexed, factorial 1307674368000) => [0,1,2,3,4,5,6,7,8,9,10,11,13,12,14]


Last updated: Jan 24 2025 at 00:11 UTC