Ejemplo n.º 1
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
Ejemplo n.º 2
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)
Ejemplo n.º 3
0
def test_constructor_with_blank_function():
    def sum(x, y):
        return x + y

    store = Store()
    function = Function(store, sum,
                        FunctionType([Type.I32, Type.I32], [Type.I32]))
Ejemplo n.º 4
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
Ejemplo n.º 5
0
def test_constructor_with_annotated_function():
    store = Store()

    def sum(a: int, b: 'i32', c: 'I32', d: 'i64', e: 'I64', f: float, g: 'f32', h: 'F32', i: 'f64', j: 'F64') -> int:
        return a + b

    function = Function(store, sum)
    function_type = function.type

    assert function_type.params == [Type.I32, Type.I32, Type.I32, Type.I64, Type.I64, Type.F32, Type.F32, Type.F32, Type.F64, Type.F64]
    assert function_type.results == [Type.I32]

    def return_none(a: int) -> None:
        pass

    function = Function(store, return_none)
    function_type = function.type

    assert function_type.params == [Type.I32]
    assert function_type.results == []

    def take_none(a: None):
        pass

    with pytest.raises(RuntimeError) as context_manager:
        Function(store, take_none)

    exception = context_manager.value
    assert str(exception) == 'Variable `a` cannot have type `None`'

    def tuple(a: int) -> (int, int):
        return (a, a)

    function = Function(store, tuple)
    function_type = function.type

    assert function_type.params == [Type.I32]
    assert function_type.results == [Type.I32, Type.I32]
Ejemplo n.º 6
0
def test_import_function_defaultdict():
    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 = defaultdict(dict)
    import_object["math"]["sum"] = Function(store, sum)

    instance = Instance(module, import_object)

    assert instance.exports.add_one(1) == 2
Ejemplo n.º 7
0
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
Ejemplo n.º 8
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)
Ejemplo n.º 9
0
# 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.
def sum(x, y):
    return x + y


sum_host_function = Function(
    store,
    sum,
    #             x         y           result
    FunctionType([Type.I32, Type.I32], [Type.I32]))
Ejemplo n.º 10
0
# Create a store. Engines and compilers are explained in other
# examples.
store = Store(engine.Universal(Compiler))

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


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


host_function = Function(store, host_function_implementation)

# 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.
#
Ejemplo n.º 11
0
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:
    assert False
Ejemplo n.º 12
0
    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))

print('current_width:', instance.exports.current_width())
assert instance.exports.current_width() == 2
Ejemplo n.º 13
0
def test_constructor_with_annotated_function():
    def sum(x: int, y: int) -> int:
        return x + y

    store = Store()
    function = Function(store, sum)
Ejemplo n.º 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