Example #1
0
    def __init__(self, engine: Engine, wasm: typing.Union[str, bytes]):
        if not isinstance(engine, Engine):
            raise TypeError("expected an Engine")

        # If this looks like a string, parse it as the text format. Note that
        # in python 2 strings and bytes are basically the same, so we skip this
        # if the first byte in the string is 0, meaning this is actually a wasm
        # module.
        if isinstance(wasm, str) and len(wasm) > 0 and ord(wasm[0]) != 0:
            wasm = wat2wasm(wasm)
        if isinstance(wasm, bytes) and len(wasm) > 0 and wasm[0] != 0:
            wasm = wat2wasm(wasm)

        if not isinstance(wasm, (bytes, bytearray)):
            raise TypeError("expected wasm bytes")

        # TODO: can the copy be avoided here? I can't for the life of me
        # figure this out.
        c_ty = c_uint8 * len(wasm)
        binary = ffi.wasm_byte_vec_t(len(wasm), c_ty.from_buffer_copy(wasm))
        ptr = POINTER(ffi.wasm_module_t)()
        error = ffi.wasmtime_module_new(engine._ptr, byref(binary), byref(ptr))
        if error:
            raise WasmtimeError._from_ptr(error)
        self._ptr = ptr
        self._owner = None
Example #2
0
    def deserialize(cls, engine: Engine,
                    encoded: typing.Union[bytes, bytearray]) -> 'Module':
        """
        Deserializes bytes previously created by `Module.serialize`.

        This constructor for `Module` will deserialize bytes previously created
        by a serialized module. This will only succeed if the bytes were
        previously created by the same version of `wasmtime` as well as the
        same configuration within `Engine`.
        """

        if not isinstance(engine, Engine):
            raise TypeError("expected an Engine")
        if not isinstance(encoded, (bytes, bytearray)):
            raise TypeError("expected bytes")

        # TODO: can the copy be avoided here? I can't for the life of me
        # figure this out.
        c_ty = c_uint8 * len(encoded)
        binary = ffi.wasm_byte_vec_t(len(encoded),
                                     c_ty.from_buffer_copy(encoded))
        ptr = POINTER(ffi.wasm_module_t)()
        error = ffi.wasmtime_module_deserialize(engine._ptr, byref(binary),
                                                byref(ptr))
        if error:
            raise WasmtimeError._from_ptr(error)
        ret: "Module" = cls.__new__(cls)
        ret._ptr = ptr
        ret._owner = None
        return ret
Example #3
0
def wat2wasm(wat: typing.Union[str, bytes]) -> bytearray:
    """
    Converts the [WebAssembly Text format][wat] to the binary format.

    This function is intended to be a convenience function for local
    development and you likely don't want to use it extensively in production.
    It's much faster to parse and compile the binary format than it is to
    process the text format.

    Takes a `str` as input, raises an error if it fails to parse, and returns
    a `bytes` if conversion/parsing was successful.

    >>> wat2wasm('(module)')
    bytearray(b'\\x00asm\\x01\\x00\\x00\\x00')

    [wat]: https://webassembly.github.io/spec/core/text/index.html
    """

    if isinstance(wat, str):
        wat = wat.encode('utf8')
    wat_buffer = cast(create_string_buffer(wat), POINTER(c_uint8))
    wat_bytes = ffi.wasm_byte_vec_t(len(wat), wat_buffer)
    wasm = ffi.wasm_byte_vec_t()
    error = ffi.wasmtime_wat2wasm(byref(wat_bytes), byref(wasm))
    if error:
        raise WasmtimeError._from_ptr(error)
    else:
        ret = ffi.to_bytes(wasm)
        ffi.wasm_byte_vec_delete(byref(wasm))
        return ret
Example #4
0
 def value(self, val: IntoVal) -> None:
     """
     Sets the value of this global to a new value
     """
     val = Val._convert(self.type.content, val)
     error = ffi.wasmtime_global_set(self._ptr, byref(val._unwrap_raw()))
     if error:
         raise WasmtimeError._from_ptr(error)
Example #5
0
 def define_instance(self, name: str, instance: Instance) -> None:
     if not isinstance(instance, Instance):
         raise TypeError("expected an `Instance`")
     name_raw = ffi.str_to_name(name)
     error = ffi.wasmtime_linker_define_instance(self._ptr, byref(name_raw),
                                                 instance._ptr)
     if error:
         raise WasmtimeError._from_ptr(error)
Example #6
0
 def define(self, module: str, name: str, item: AsExtern) -> None:
     raw_item = get_extern_ptr(item)
     module_raw = ffi.str_to_name(module)
     name_raw = ffi.str_to_name(name)
     error = ffi.wasmtime_linker_define(self._ptr, byref(module_raw),
                                        byref(name_raw), raw_item)
     if error:
         raise WasmtimeError._from_ptr(error)
Example #7
0
 def profiler(self, profiler: str) -> None:
     if profiler == "none":
         error = ffi.wasmtime_config_profiler_set(self._ptr, 0)
     elif profiler == "jitdump":
         error = ffi.wasmtime_config_profiler_set(self._ptr, 1)
     else:
         raise WasmtimeError("unknown profiler: " + str(profiler))
     if error:
         raise WasmtimeError._from_ptr(error)
Example #8
0
    def __call__(self,
                 *params: IntoVal) -> Union[IntoVal, Sequence[IntoVal], None]:
        """
        Calls this function with the given parameters

        Parameters can either be a `Val` or a native python value which can be
        converted to a `Val` of the corresponding correct type

        Returns `None` if this func has 0 return types
        Returns a single value if the func has 1 return type
        Returns a list if the func has more than 1 return type

        Note that you can also use the `__call__` method and invoke a `Func` as
        if it were a function directly.
        """

        ty = self.type
        param_tys = ty.params
        if len(params) > len(param_tys):
            raise WasmtimeError(
                "too many parameters provided: given %s, expected %s" %
                (len(params), len(param_tys)))
        if len(params) < len(param_tys):
            raise WasmtimeError(
                "too few parameters provided: given %s, expected %s" %
                (len(params), len(param_tys)))

        param_vals = [
            Val._convert(ty, params[i]) for i, ty in enumerate(param_tys)
        ]
        params_ptr = (ffi.wasm_val_t * len(params))()
        for i, val in enumerate(param_vals):
            params_ptr[i] = val._unwrap_raw()

        result_tys = ty.results
        results_ptr = (ffi.wasm_val_t * len(result_tys))()

        trap = POINTER(ffi.wasm_trap_t)()
        error = ffi.wasmtime_func_call(self._ptr,
                                       params_ptr, len(params), results_ptr,
                                       len(result_tys), byref(trap))
        if error:
            raise WasmtimeError._from_ptr(error)
        if trap:
            raise Trap._from_ptr(trap)

        results = []
        for i in range(0, len(result_tys)):
            results.append(Val(results_ptr[i]).value)
        if len(results) == 0:
            return None
        elif len(results) == 1:
            return results[0]
        else:
            return results
Example #9
0
 def instantiate(self, module: Module) -> Instance:
     if not isinstance(module, Module):
         raise TypeError("expected a `Module`")
     trap = POINTER(ffi.wasm_trap_t)()
     instance = POINTER(ffi.wasm_instance_t)()
     error = ffi.wasmtime_linker_instantiate(self._ptr, module._ptr,
                                             byref(instance), byref(trap))
     if error:
         raise WasmtimeError._from_ptr(error)
     if trap:
         raise Trap._from_ptr(trap)
     return Instance._from_ptr(instance, module)
Example #10
0
 def __init__(self, store: Store, ty: GlobalType, val: IntoVal):
     if not isinstance(store, Store):
         raise TypeError("expected a Store")
     if not isinstance(ty, GlobalType):
         raise TypeError("expected a GlobalType")
     val = Val._convert(ty.content, val)
     ptr = POINTER(ffi.wasm_global_t)()
     error = ffi.wasmtime_global_new(store._ptr, ty._ptr,
                                     byref(val._unwrap_raw()), byref(ptr))
     if error:
         raise WasmtimeError._from_ptr(error)
     self._ptr = ptr
     self._owner = None
Example #11
0
    def get_one_by_name(self, module: str, name: str) -> AsExtern:
        """
        Gets a singular item defined in this linker.

        Raises an error if this item hasn't been defined or if the item has been
        defined twice with different types.
        """
        module_raw = ffi.str_to_name(module)
        name_raw = ffi.str_to_name(name)
        item = POINTER(ffi.wasm_extern_t)()
        error = ffi.wasmtime_linker_get_one_by_name(self._ptr, byref(module_raw), byref(name_raw), byref(item))
        if error:
            raise WasmtimeError._from_ptr(error)
        return wrap_extern(item, None)
Example #12
0
    def serialize(self) -> bytearray:
        """
        Serializes this module to a binary representation.

        This method will serialize this module to an in-memory byte array which
        can be cached and later passed to `Module.deserialize` to recreate this
        module.
        """
        raw = ffi.wasm_byte_vec_t()
        err = ffi.wasmtime_module_serialize(self._ptr, byref(raw))
        if err:
            raise WasmtimeError._from_ptr(err)
        ret = ffi.to_bytes(raw)
        ffi.wasm_byte_vec_delete(byref(raw))
        return ret
Example #13
0
    def add_fuel(self, fuel: int) -> None:
        """
        Adds the specified amount of fuel into this store.

        This is only relevant when `Config.consume_fuel` is configured.

        This is a required call to ensure that the store has fuel to
        execute WebAssembly since otherwise stores start with zero fuel.

        Raises a `WasmtimeError` if this store's configuration is not configured
        to consume fuel.
        """
        err = ffi.wasmtime_store_add_fuel(self._ptr, fuel)
        if err:
            raise WasmtimeError._from_ptr(err)
Example #14
0
    def get_default(self, name: str) -> Func:
        """
        Gets the default export for the named module in this linker.

        For more information on this see the Rust documentation at
        https://docs.wasmtime.dev/api/wasmtime/struct.Linker.html#method.get_default.

        Raises an error if the default export wasn't present.
        """
        name_raw = ffi.str_to_name(name)
        default = POINTER(ffi.wasm_func_t)()
        error = ffi.wasmtime_linker_get_default(self._ptr, byref(name_raw), byref(default))
        if error:
            raise WasmtimeError._from_ptr(error)
        return Func._from_ptr(default, None)
Example #15
0
    def define_wasi(self, instance: WasiInstance) -> None:
        """
        Defines a WASI instance in this linker.

        The instance provided has been previously constructed and this method
        will define all the appropriate imports and their names into this linker
        to assist with instantiating modules that use WASI.

        This function will raise an error if shadowing is disallowed and a name
        was previously defined.
        """
        if not isinstance(instance, WasiInstance):
            raise TypeError("expected an `WasiInstance`")
        error = ffi.wasmtime_linker_define_wasi(self._ptr, instance._ptr)
        if error:
            raise WasmtimeError._from_ptr(error)
Example #16
0
    def define_instance(self, name: str, instance: Instance) -> None:
        """
        Convenience wrapper to define an entire instance in this linker.

        This function will `define` eaech of the exports on the instance into
        this linker, using the name provided as the module name and the export's
        own name as the field name.

        This function will raise an error if `instance` comes from the wrong
        store or if shadowing is disallowed and a name was previously defined.
        """
        if not isinstance(instance, Instance):
            raise TypeError("expected an `Instance`")
        name_raw = ffi.str_to_name(name)
        error = ffi.wasmtime_linker_define_instance(self._ptr, byref(name_raw),
                                                    instance._ptr)
        if error:
            raise WasmtimeError._from_ptr(error)
Example #17
0
    def define_module(self, name: str, module: Module) -> None:
        """
        Defines automatic instantiations of the provided module in this linker.

        The `module` provided is defined under `name` with automatic
        instantiations which respect WASI Commands and Reactors.

        For more information see the Rust documentation at
        https://docs.wasmtime.dev/api/wasmtime/struct.Linker.html#method.module.

        This method will throw an error if shadowing is disallowed and an item
        has previously been defined.
        """
        if not isinstance(module, Module):
            raise TypeError("expected a `Module`")
        name_raw = ffi.str_to_name(name)
        error = ffi.wasmtime_linker_module(self._ptr, byref(name_raw), module._ptr)
        if error:
            raise WasmtimeError._from_ptr(error)
Example #18
0
    def validate(cls, store: Store, wasm: typing.Union[bytes,
                                                       bytearray]) -> None:
        """
        Validates whether the list of bytes `wasm` provided is a valid
        WebAssembly binary given the configuration in `store`

        Raises a `WasmtimeError` if the wasm isn't valid.
        """

        if not isinstance(store, Store):
            raise TypeError("expected a Store")
        if not isinstance(wasm, (bytes, bytearray)):
            raise TypeError("expected wasm bytes")

        # TODO: can the copy be avoided here? I can't for the life of me
        # figure this out.
        c_ty = c_uint8 * len(wasm)
        binary = ffi.wasm_byte_vec_t(len(wasm), c_ty.from_buffer_copy(wasm))
        error = ffi.wasmtime_module_validate(store._ptr, byref(binary))
        if error:
            raise WasmtimeError._from_ptr(error)
Example #19
0
    def strategy(self, strategy: str) -> None:
        """
        Configures the compilation strategy used for wasm code.

        Acceptable values for `strategy` are:

        * `"auto"`
        * `"cranelift"`
        * `"lightbeam"`
        """

        if strategy == "auto":
            error = ffi.wasmtime_config_strategy_set(self._ptr, 0)
        elif strategy == "cranelift":
            error = ffi.wasmtime_config_strategy_set(self._ptr, 1)
        elif strategy == "lightbeam":
            error = ffi.wasmtime_config_strategy_set(self._ptr, 2)
        else:
            raise WasmtimeError("unknown strategy: " + str(strategy))
        if error:
            raise WasmtimeError._from_ptr(error)
Example #20
0
    def define(self, module: str, name: str, item: AsExtern) -> None:
        """
        Defines a new item, by name, in this linker.

        This method will add a new definition to this linker. The `module` nad
        `name` provided are what to name the `item` within the linker.

        This function will raise an error if `item` comes from the wrong store
        or if shadowing is disallowed and the module/name pair has already been
        defined.
        """
        raw_item = get_extern_ptr(item)
        module_raw = ffi.str_to_name(module)
        name_raw = ffi.str_to_name(name)
        error = ffi.wasmtime_linker_define(
            self._ptr,
            byref(module_raw),
            byref(name_raw),
            raw_item)
        if error:
            raise WasmtimeError._from_ptr(error)
Example #21
0
    def instantiate(self, module: Module) -> Instance:
        """
        Instantiates a module using this linker's defined set of names.

        This method will attempt to satisfy all the imports of the `module`
        provided with the names defined within this linker. If all names are
        defined then the module is instantiated.

        Raises an error if an import of `module` hasn't been defined in this
        linker or if a trap happens while instantiating the instance.
        """
        if not isinstance(module, Module):
            raise TypeError("expected a `Module`")
        trap = POINTER(ffi.wasm_trap_t)()
        instance = POINTER(ffi.wasm_instance_t)()
        error = ffi.wasmtime_linker_instantiate(
            self._ptr, module._ptr, byref(instance), byref(trap))
        if error:
            raise WasmtimeError._from_ptr(error)
        if trap:
            raise Trap._from_ptr(trap)
        return Instance._from_ptr(instance, None)
Example #22
0
    def __init__(self, store: Store, module: Module, imports: Sequence[AsExtern]):
        """
        Creates a new instance by instantiating the `module` given with the
        `imports` into the `store` provided.

        The `store` must have type `Store`, the `module` must have type
        `Module`, and the `imports` must be an iterable of external values,
        either `Extern`, `Func`, `Table`, `Memory`, or `Global`.

        Raises an error if instantiation fails (e.g. linking or trap) and
        otherwise initializes the new instance.
        """

        if not isinstance(store, Store):
            raise TypeError("expected a Store")
        if not isinstance(module, Module):
            raise TypeError("expected a Module")

        imports_ptr = (POINTER(ffi.wasm_extern_t) * len(imports))()
        for i, val in enumerate(imports):
            imports_ptr[i] = get_extern_ptr(val)

        instance = POINTER(ffi.wasm_instance_t)()
        trap = POINTER(ffi.wasm_trap_t)()
        error = ffi.wasmtime_instance_new(
            store._ptr,
            module._ptr,
            imports_ptr,
            len(imports),
            byref(instance),
            byref(trap))
        if error:
            raise WasmtimeError._from_ptr(error)
        if trap:
            raise Trap._from_ptr(trap)
        self._ptr = instance
        self._module = module
        self._exports = None
Example #23
0
    def cache(self, enabled: typing.Union[bool, str]) -> None:
        """
        Configures whether code caching is enabled for this `Config`.

        The value `True` can be passed in here to enable the default caching
        configuration and location, or a path to a file can be passed in which
        is a path to a TOML configuration file for the cache.

        More information about cache configuration can be found at
        https://bytecodealliance.github.io/wasmtime/cli-cache.html
        """

        if isinstance(enabled, bool):
            if not enabled:
                raise WasmtimeError("caching cannot be explicitly disabled")
            error = ffi.wasmtime_config_cache_config_load(self._ptr, None)
        elif isinstance(enabled, str):
            error = ffi.wasmtime_config_cache_config_load(self._ptr,
                                                          c_char_p(enabled.encode('utf-8')))
        else:
            raise TypeError("expected string or bool")
        if error:
            raise WasmtimeError._from_ptr(error)
Example #24
0
 def define_wasi(self, instance: WasiInstance) -> None:
     if not isinstance(instance, WasiInstance):
         raise TypeError("expected an `WasiInstance`")
     error = ffi.wasmtime_linker_define_wasi(self._ptr, instance._ptr)
     if error:
         raise WasmtimeError._from_ptr(error)