pywasm

repository·master·Indexed 19 days ago

https://github.com/libraries/pywasm

A pure Python WebAssembly interpreter with no third-party dependencies. It supports the WebAssembly 2.0 (Draft 2025-04-25) specification and WASI Preview 1. The library provides a core execution engine, a command-line interface for running WASM/WASI modules, and specialized arithmetic types (I, U, F32, F64) that adhere to WebAssembly requirements.

Tokens
5K
Snippets
14
Records
21
Agent score
68%

What's inside pywasm

  1. Instantiate and invoke WebAssembly modules

    master

    To run a WebAssembly module, use pywasm.core.Runtime to create a runtime instance. You can instantiate modules directly from a file using the instance_from_file method. Once instantiated, use invocate to call specific exported functions from the module.

    Note: invocate returns a list/tuple of results, so access the desired return value via index (e.g., r[0]).

    import pywasm
    pywasm.log.lvl = 1
    
    runtime = pywasm.core.Runtime()
    # Load the module from a file path
    m = runtime.instance_from_file('example/fibonacci/bin/fibonacci.wasm')
    # Call the 'fibonacci' function with argument [10]
    r = runtime.invocate(m, 'fibonacci', [10])
    print(f'fibonacci(10) = {r[0]}')
  2. Run tests for pywasm

    master

    Before running tests, you may need to download external tools and test suites using the provided build scripts.

    1. Download wabt tools: python script/build_wabt.py
    2. Download spec tests: python script/build_spec.py
    3. Download WASI testsuite: python script/build_wasi.py

    Then you can run the following test suites:

    • python test/example.py (Example tests)
    • python test/main.py (Main tests)
    • python test/spec.py (Specification tests)
    • python test/wasi.py (WASI tests)
    $ python script/build_wabt.py
    $ python script/build_spec.py
    $ python script/build_wasi.py
    
    $ python test/example.py
    $ python test/main.py
    $ python test/spec.py
    $ python test/wasi.py
  3. How WASI Preview1 execution starts via main()

    master

    To execute a WebAssembly module as a WASI command, the main method is used. It attempts to invoke the _start export of the provided module using the runtime.invocate method.

    Lifecycle and Cleanup:

    • If _start succeeds, it returns 0.
    • If _start raises a SystemExit, the corresponding exit code is returned.
    • Upon completion (success or failure), the implementation performs cleanup:
      • Resets pipes for standard streams (FD_STDOUT to FD_STDERR).
      • Closes any open host file descriptors that are not character devices.
    # Conceptual usage of the main entrypoint logic
    result = wasi_instance.main(runtime, module_instance)
    if result == 0:
        print("Program exited successfully")
    else:
        print(f"Program exited with code {result}")
  4. WASI Preview1 Polling with poll_oneoff

    master

    The poll_oneoff method allows for concurrent polling of multiple events. It supports three types of subscriptions:

    1. Clock Events (EVENTTYPE_CLOCK): Polls for a specific time.
    2. FD Read Events (EVENTTYPE_FD_READ): Polls for readiness to read from a file descriptor.
    3. FD Write Events (EVENTTYPE_FD_WRITE): Polls for readiness to write to a file descriptor.

    Arguments passed to the method include pointers to subscription arrays (subs), output arrays (outs), and the number of subscriptions (nsub) and outputs (nout). The method uses select.select internally to wait for events with a calculated timeout based on clock subscriptions.

  5. Import pywasm and access core functionality

    master

    The pywasm package provides a WebAssembly interpreter written in pure Python. By importing the package, you gain access to the core WebAssembly execution engine and its submodules. The package exports all members from pywasm.core directly into the top-level namespace.

    Key submodules available for use include:

    • arith: Arithmetic operations.
    • core: The primary WebAssembly engine and execution logic.
    • leb128: LEB128 integer encoding/decoding.
    • log: Logging utilities.
    • opcode: WebAssembly opcode definitions.
    • wasi: WebAssembly System Interface (available on Darwin and Linux).
    import pywasm
    
    # Access core functionality exported from pywasm.core
    # Example: pywasm.some_core_function()
  6. WASI Preview1 Socket Operations

    master

    Pywasm implements socket-based networking for WASI Preview1. These operations support both FILETYPE_SOCKET_STREAM (TCP-like) and FILETYPE_SOCKET_DGRAM (UDP-like) types.

    Supported socket methods:

    • sock_accept: Accepts an incoming connection on a listening socket.
    • sock_recv: Receives data from a socket into iovs buffers.
    • sock_send: Sends data from iovs buffers through a socket.
    • sock_shutdown: Shuts down the send or receive channels of a socket.

    Errors such as self.ERRNO_NOTSOCK are returned if operations are attempted on non-socket file descriptors.

  7. Enable debug logging with debugln

    master

    The debugln function prints messages only if the global lvl variable in pywasm.log is set to a value greater than 0. If lvl is 0 (the default), debugln calls will have no effect. You can enable debug output by manually setting pywasm.log.lvl.

    import pywasm.log
    
    # By default, debugln does nothing
    pywasm.log.debugln("This won't show")
    
    # Enable debug logging
    pywasm.log.lvl = 1
    pywasm.log.debugln("This will show")
  8. Use floating-point types (F32 and F64) for WebAssembly arithmetic

    master

    The F32 and F64 classes handle WebAssembly-compliant floating-point operations.

    • F32: Uses ctypes.c_float to ensure single-precision behavior. Operations like div, min, and max are implemented via F64 and then cast back to F32 using fit().
    • F64: Implements double-precision arithmetic. The div method includes specific handling for division by zero to return signed infinity or NaN according to IEEE 754/WebAssembly standards. min and max are designed to handle NaN values correctly (if either operand is NaN, the result is NaN).
    • Serialization: Both types support from_bytearray and into_bytearray using little-endian format.
    from pywasm.arith import f32, f64
    
    # F32 operations
    val_f32 = f32.from_bytearray(bytearray([0, 0, 128, 63])) # 1.0
    
    # F64 division by zero
    inf_val = f64.div(1.0, 0.0) # Returns inf
    nan_val = f64.div(float('nan'), 1.0) # Returns nan
  9. WASI Preview1 File Descriptor Operations

    master

    Pywasm implements several WASI Preview1 file descriptor (FD) operations. These methods interact with the host's file system and manage the lifecycle of file descriptors within the WebAssembly environment.

    Key operations include:

    • fd_renumber: Renames a file descriptor.
    • fd_seek: Moves the offset of a file descriptor.
    • fd_sync: Synchronizes data and metadata to disk.
    • fd_tell: Returns the current offset of a file descriptor.
    • fd_write: Writes data to a file descriptor using iovs (I/O vectors).
    • fd_read (implied by usage patterns): Reads data from a file descriptor.

    Note: These methods return a list containing an error code (e.g., self.ERRNO_SUCCESS, self.ERRNO_BADF, self.ERRNO_ISDIR, self.ERRNO_NOTCAPABLE).

  10. Use unsigned integer types (U) for WebAssembly arithmetic

    master

    The U class provides unsigned integer arithmetic. It includes additional bit manipulation utilities common in WebAssembly.

    Key features:

    • Wrapping Arithmetic: add, sub, and mul wrap using fit().
    • Saturating Arithmetic: add_sat and sub_sat clamp to [0, max].
    • Bit Manipulation:
      • rotl / rotr: Bitwise rotation.
      • clz: Count leading zeros.
      • ctz: Count trailing zeros.
      • popcnt: Population count (number of set bits).
    • Serialization: from_bytearray and into_bytearray use little-endian unsigned logic.
    from pywasm.arith import u32
    
    # Bitwise rotation
    val = u32.rotl(0x00000001, 8)  # Result: 0x00000100
    
    # Population count
    count = u32.popcnt(0b1011)     # Result: 3
    
    # Count leading zeros
    leading = u32.clz(0x00000001)  # Result: 31
  11. WASI Preview1 Process and Scheduling Operations

    master

    Pywasm provides interfaces for process control and concurrency:

    • proc_exit: Terminates the process with a specific exit code. If self.return_on_exit is enabled, it raises a SystemExit exception.
    • proc_raise: Sends a signal to the calling thread (currently only supports self.SIGNAL_NONE).
    • sched_yield: Temporarily yields execution of the calling thread.
    • random_get: Fills a buffer with high-quality random bytes using random.randbytes.