Example #1
0
def test_return_multiple_values_from_host_function():
    store = Store()
    module = Module(
        store,
        """
        (module
          (type $swap_t (func (param i32 i64) (result i64 i32)))
          (type $test_t (func (param i32 i64) (result i64 i32)))

          (import "env" "swap" (func $swap (type $swap_t)))

          (func $test (type $test_t) (param $x i32) (param $y i64) (result i64 i32)
            local.get $x
            local.get $y
            call $swap)
          (export "test" (func $test)))
        """
    )

    def swap(x: 'i32', y: 'i64') -> ('i64', 'i32'):
        return (y, x)

    import_object = ImportObject()
    import_object.register(
        "env",
        {
            "swap": Function(store, swap),
        }
    )
    instance = Instance(module, import_object)

    assert instance.exports.test(41, 42) == (42, 41)
def test_import_global():
    store = Store()
    module = Module(
        store, """
        (module
          (import "env" "global" (global $global (mut i32)))
          (func (export "read_g") (result i32)
            global.get $global)
          (func (export "write_g") (param i32)
            local.get 0
            global.set $global))
        """)

    global_ = Global(store, Value.i32(7), mutable=True)

    import_object = ImportObject()
    import_object.register("env", {"global": global_})

    instance = Instance(module, import_object)

    assert instance.exports.read_g() == 7
    global_.value = 153
    assert instance.exports.read_g() == 153
    instance.exports.write_g(11)
    assert global_.value == 11
def test_import_memory():
    store = Store()
    module = Module(
        store, """
        (module
          (import "env" "memory" (memory $memory 1))
          (func (export "increment")
            i32.const 0
            i32.const 0
            i32.load    ;; load 0
            i32.const 1
            i32.add     ;; add 1
            i32.store   ;; store at 0
            ))
        """)

    memory = Memory(store, MemoryType(1, shared=False))
    view = memory.uint8_view(offset=0)

    import_object = ImportObject()
    import_object.register("env", {"memory": memory})

    instance = Instance(module, import_object)

    assert view[0] == 0
    instance.exports.increment()
    assert view[0] == 1
    instance.exports.increment()
    assert view[0] == 2
Example #4
0
def test_early_exit():
    store = Store()
    module = Module(
        store, """
        (module
          (type $run_t (func (param i32 i32) (result i32)))
          (type $early_exit_t (func (param) (result)))

          (import "env" "early_exit" (func $early_exit (type $early_exit_t)))

          (func $run (type $run_t) (param $x i32) (param $y i32) (result i32)
            (call $early_exit)
            (i32.add
                local.get $x
                local.get $y))

          (export "run" (func $run)))
        """)

    def early_exit():
        raise Exception('oops')

    import_object = ImportObject()
    import_object.register("env", {
        "early_exit": Function(store, early_exit),
    })
    instance = Instance(module, import_object)

    try:
        instance.exports.run(1, 2)
    except RuntimeError as err:
        assert 'oops' in str(err)
    else:
        assert False
Example #5
0
def host_functions(store: Store) -> ImportObject:
    """
    Provide an "abort()" host function.
    https://github.com/flow-heater/pytshello/issues/1

    :param store:
    :return:
    """

    # When creating an `Instance`, we can pass an `ImportObject`. All
    # entities that must be imported are registered inside the
    # `ImportObject`.
    import_object = ImportObject()

    # Let's write the Python function that is going to be imported,
    # i.e. called by the WebAssembly module.
    def abort(a: 'i32', b: 'i32', c: 'i32', d: 'i32'):
        pass

    abort_host_function = Function(store, abort)

    # Now let's register the `sum` import inside the `env` namespace.
    import_object.register("env", {
        "abort": abort_host_function,
    })

    return import_object
def test_import_function():
    def sum(x: int, y: int) -> int:
        return x + y

    store = Store()
    module = Module(
        store, """
        (module
          (import "math" "sum" (func $sum (param i32 i32) (result i32)))
          (func (export "add_one") (param i32) (result i32)
            local.get 0
            i32.const 1
            call $sum))
        """)

    import_object = ImportObject()
    import_object.register("math", {"sum": Function(store, sum)})

    instance = Instance(module, import_object)

    assert instance.exports.add_one(1) == 2
Example #7
0
def build_instance():
    import_object = ImportObject()

    store = Store(engine.JIT(Compiler))

    import_object.register(
        "env", {
            "__sys_getpid":
            Function(store, lambda: 42, FunctionType([], [Type.I32])),
        })

    import_object.register(
        "wasi_snapshot_preview1", {
            "proc_exit":
            Function(store, lambda *args: None, FunctionType([Type.I32], [])),
            "clock_time_get":
            Function(store, lambda *args: int(time.time()),
                     FunctionType([Type.I32, Type.I64, Type.I32], [Type.I32])),
            "fd_close":
            Function(store, lambda *args: 1,
                     FunctionType([Type.I32], [Type.I32])),
            "fd_write":
            Function(
                store, lambda *args: 1,
                FunctionType([Type.I32, Type.I32, Type.I32, Type.I32],
                             [Type.I32])),
            "fd_seek":
            Function(
                store, lambda *args: 1,
                FunctionType([Type.I32, Type.I64, Type.I32, Type.I32],
                             [Type.I32])),
            "fd_read":
            Function(
                store, lambda *args: 1,
                FunctionType([Type.I32, Type.I32, Type.I32, Type.I32],
                             [Type.I32])),
        })

    # Let's compile the module to be able to execute it!
    module = Module(store, open(quiet_path, 'rb').read())

    # Now the module is compiled, we can instantiate it.
    return Instance(module, import_object)
Example #8
0
        i32.const 1
        call $sum))
    """)

# Create a store.
store = Store(engine.Universal(Compiler))

# Let's compile the Wasm module.
module = Module(store, wasm_bytes)

# Here we go.
#
# When creating an `Instance`, we can pass an `ImportObject`. All
# entities that must be imported are registered inside the
# `ImportObject`.
import_object = ImportObject()


# Let's write the Python function that is going to be imported,
# i.e. called by the WebAssembly module.
def sum(x: int, y: int) -> int:
    return x + y


sum_host_function = Function(store, sum)


# See how we have used Python annotations to help `wasmer` to infer
# the types of the host function? Well, it could be limited. For
# example, `int` in Python matches to `i32` in WebAssembly. We can't
# represent `i64`. Or… we can use a function type.
Example #9
0
# Let's then create a global that is going to be imported.
host_global = Global(store, Value.i32(42))

# Create an import object.
#
# Imports are stored in namespaces. We'll need to register each of the
# namespaces with a name and add the imported entities there.
#
# Note that the namespace can also have an empty name.
#
# Our module requires us to import:
#   * A function `host_function` in a namespace with an empty name;
#   * A global `host_global` in the `env` namespace.
#
# Let's do this!
import_object = ImportObject()
import_object.register("", {
    "host_function": host_function,
})
import_object.register("env", {
    "host_global": host_global,
})

# Let's instantiate the module!
instance = Instance(module, import_object)

# And finally, let's play.
#
# The Wasm module exports some entities:
#
# * A function: `guest_function`,
def test_contains_namespace():
    import_object = ImportObject()

    assert import_object.contains_namespace("foo") == False
def test_constructor():
    import_object = ImportObject()
Example #12
0
# Let's compile the Wasm module.
module = Module(store, wasm_bytes)


# Here we go.
#
# Let's write the Python function that is going to… fail!
def early_exit():
    raise Exception('oops')


# When creating an `Instance`, we can pass an `ImportObject`. All
# entities that must be imported are registered inside the
# `ImportObject`.
import_object = ImportObject()

# Now let's register the `sum` import inside the `env` namespace.
import_object.register("env", {
    "early_exit": Function(store, early_exit),
})

# Let's instantiate the module!
instance = Instance(module, import_object)

# And finally, call the `run` exported function!
try:
    instance.exports.run(1, 2)
except RuntimeError as err:
    assert 'oops' in str(err)
else:
Example #13
0
def handle_band(width: int) -> int:
    print('got a line of:', width)
    assert width == 7
    return 0


def mode_parsed(mode: int) -> int:
    print('mode selected:', mode)
    assert mode == 1
    return 0


# load wasm engine
store = Store(engine.JIT(Compiler))
module = Module(store, open('./decoder.wasm', 'rb').read())
import_object = ImportObject()
import_object.register(
    "env", {
        "handle_band": Function(store, handle_band),
        'mode_parsed': Function(store, mode_parsed),
    })
instance = Instance(module, import_object)
mem = instance.exports.memory.int8_view()
chunk_address = instance.exports.get_chunk_address()

# load test data
mem[chunk_address:] = TEST

# run
instance.exports.init(-1, 0, 256, 1)
instance.exports.decode(0, len(TEST))
Example #14
0
def make_import_object(store):

    INVALID_HANDLE_ERROR = 'Invalid array handle: '
    INVALID_BOUNDS_ERROR = 'Array index out of bounds: '
    HANDLES = []
    VALID_INT_REGEX = compile(r'^\s*-?\d+\s*$')

    def check_bounds(a, i, error_message, fun_name):
        """Checks if i is within bounds of array a,
        otherwise throws an exception with a given error_message.
        """
        if not (0 <= i < len(a)):
            print(f'Runtime error in function {fun_name}. {error_message}',
                  file=sys.stderr)
            sys.exit(1)

    #----------------------------------------------------------------
    # Functions to be imported from the Wasm module

    def printi(i: int) -> int:
        """Prints i to stdout as a decimal integer. Does not print a
        new line at the end. Returns 0.
        """
        print(i, end='')
        return 0

    def printc(c: int) -> int:
        """Prints a character to stdout, where c is its Unicode code
        point. Does not print a new line at the end. Returns 0.
        """
        print(chr(c), end='')
        return 0

    def prints(s: int) -> int:
        """Prints s to stdout as a string. s must be a handle to an
        array list containing zero or more Unicode code points.
        Does not print a new line at the end. Returns 0.
        """
        check_bounds(HANDLES, s, INVALID_HANDLE_ERROR + str(s),
                     currentframe().f_code.co_name)
        print(''.join([chr(c) for c in HANDLES[s]]), end='')
        return 0

    def println() -> int:
        """Prints a newline character to stdout. Returns 0.
        """
        print()
        return 0

    def readi() -> int:
        """Reads from stdin a signed decimal integer and return its
        value. Does not return until a valid integer has been read.
        """
        data = ''
        while not VALID_INT_REGEX.match(data):
            data = input()
        return int(data)

    def reads() -> int:
        """
        Reads from stdin a string (until the end of line) and returns
        a handle to a newly created array list containing the Unicode
        code points of all the characters read, excluding the end of
        line.
        """
        data = input()
        HANDLES.append([ord(c) for c in data])
        return len(HANDLES) - 1

    def new(n: int) -> int:
        """Creates a new array list object with n elements and returns
        its handle. All the elements of the array list are set to
        zero. Throws an exception if n is less than zero.
        """
        if n < 0:
            print(
                'Runtime error in function new. '
                f"Can't create a negative size array: {n}",
                file=sys.stderr)
            sys.exit(1)
        HANDLES.append([0] * n)
        return len(HANDLES) - 1

    def size(h: int) -> int:
        """Returns the size (number of elements) of the array list
        referenced by handle h. Throws an exception if h is not
        a valid handle.
        """
        check_bounds(HANDLES, h, INVALID_HANDLE_ERROR + str(h),
                     currentframe().f_code.co_name)
        return len(HANDLES[h])

    def add(h: int, x: int) -> int:
        """Adds x at the end of the array list referenced by handle h.
        Returns 0. Throws an exception if h is not a valid handle.
        """
        check_bounds(HANDLES, h, INVALID_HANDLE_ERROR + str(h),
                     currentframe().f_code.co_name)
        HANDLES[h].append(x)
        return 0

    def get(h: int, i: int) -> int:
        """Returns the value at index i from the array list referenced by
        handle h. Throws an exception if i is out of bounds or if h is
        not a valid handle.
        """
        check_bounds(HANDLES, h, INVALID_HANDLE_ERROR + str(h),
                     currentframe().f_code.co_name)
        check_bounds(HANDLES[h], i, INVALID_BOUNDS_ERROR + str(i),
                     currentframe().f_code.co_name)
        return HANDLES[h][i]

    def set(h: int, i: int, x: int) -> int:
        """Sets to x the element at index i of the array list referenced
        by handle h. Returns 0. Throws an exception if i is out of
        bounds or if h is not a valid handle.
        """
        check_bounds(HANDLES, h, INVALID_HANDLE_ERROR + str(h),
                     currentframe().f_code.co_name)
        check_bounds(HANDLES[h], i, INVALID_BOUNDS_ERROR + str(i),
                     currentframe().f_code.co_name)
        HANDLES[h][i] = x
        return 0

    #----------------------------------------------------------------

    import_object = ImportObject()

    import_object.register(
        "drac", {
            "printi": Function(store, printi),
            "printc": Function(store, printc),
            "prints": Function(store, prints),
            "println": Function(store, println),
            "readi": Function(store, readi),
            "reads": Function(store, reads),
            "new": Function(store, new),
            "size": Function(store, size),
            "add": Function(store, add),
            "get": Function(store, get),
            "set": Function(store, set),
        })

    return import_object