Example #1
0
    def __getitem__(self, idx: int) -> Optional[Any]:
        """
        Gets an individual element within this table.

        Returns `None` for null references in the table (i.e. a null `funcref`
        or a null `externref).

        Returns a `Func` for non-null `funcref` table elements.

        Returns the wrapped extern data for non-null `externref` table elements.

        Raises an `WasmtimeError` if `idx` is out of bounds.
        """
        if idx >= self.size:
            raise WasmtimeError("table index out of bounds")

        if self.type.element == ValType.externref():
            val = Val.externref(None)
        elif self.type.element == ValType.funcref():
            val = Val.funcref(None)
        else:
            raise WasmtimeError("unsupported table element type")

        val._unwrap_raw().of.ref = ffi.wasm_table_get(self._ptr, idx)
        return val.value
Example #2
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 #3
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 #4
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 #5
0
def wrap_extern(ptr: 'pointer[ffi.wasm_extern_t]',
                owner: Optional[Any]) -> AsExtern:
    from wasmtime import Func, Table, Global, Memory, Module, Instance

    if not isinstance(ptr, POINTER(ffi.wasm_extern_t)):
        raise TypeError("wrong pointer type")

    # We must free this as an extern, so if there's no ambient owner then
    # configure an owner with the right destructor
    if owner is None:
        owner = Extern(ptr)

    val = ffi.wasm_extern_as_func(ptr)
    if val:
        return Func._from_ptr(val, owner)
    val = ffi.wasm_extern_as_table(ptr)
    if val:
        return Table._from_ptr(val, owner)
    val = ffi.wasm_extern_as_global(ptr)
    if val:
        return Global._from_ptr(val, owner)
    val = ffi.wasm_extern_as_memory(ptr)
    if val:
        return Memory._from_ptr(val, owner)
    val = ffi.wasm_extern_as_instance(ptr)
    if val:
        return Instance._from_ptr(val, owner)
    val = ffi.wasm_extern_as_module(ptr)
    if val:
        return Module._from_ptr(val, owner)
    raise WasmtimeError("unknown extern")
Example #6
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 #7
0
 def __init__(self, store: Store):
     if not isinstance(store, Store):
         raise TypeError("expected a Store")
     ptr = ffi.wasmtime_interrupt_handle_new(store._ptr)
     if not ptr:
         raise WasmtimeError("interrupts not enabled on Store")
     self._ptr = ptr
Example #8
0
    def __init__(self, store: Store, ty: FuncType, func: Callable, access_caller: bool = False):
        """
        Creates a new func in `store` with the given `ty` which calls the closure
        given

        The `func` is called with the parameters natively and they'll have native
        Python values rather than being wrapped in `Val`. If `access_caller` is
        set to `True` then the first argument given to `func` is an instance of
        type `Caller` below.
        """

        if not isinstance(store, Store):
            raise TypeError("expected a Store")
        if not isinstance(ty, FuncType):
            raise TypeError("expected a FuncType")
        idx = FUNCTIONS.allocate((func, ty.results, store))
        if access_caller:
            ptr = ffi.wasmtime_func_new_with_env(
                store._ptr,
                ty._ptr,
                trampoline_with_caller,
                idx,
                finalize)
        else:
            ptr = ffi.wasm_func_new_with_env(
                store._ptr, ty._ptr, trampoline, idx, finalize)
        if not ptr:
            FUNCTIONS.deallocate(idx)
            raise WasmtimeError("failed to create func")
        self._ptr = ptr
        self._owner = None
Example #9
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 #10
0
 def __init__(self, limits: Limits):
     if not isinstance(limits, Limits):
         raise TypeError("expected Limits")
     ptr = ffi.wasm_memorytype_new(byref(limits.__ffi__()))
     if not ptr:
         raise WasmtimeError("failed to allocate MemoryType")
     self._ptr = ptr
     self._owner = None
Example #11
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 #12
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 #13
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 #14
0
 def __init__(self, valtype: ValType, limits: Limits):
     if not isinstance(limits, Limits):
         raise TypeError("expected Limits")
     type_ptr = take_owned_valtype(valtype)
     ptr = ffi.wasm_tabletype_new(type_ptr, byref(limits.__ffi__()))
     if not ptr:
         raise WasmtimeError("failed to allocate TableType")
     self._ptr = ptr
     self._owner = None
Example #15
0
 def cranelift_opt_level(self, opt_level: str) -> None:
     if opt_level == "none":
         ffi.wasmtime_config_cranelift_opt_level_set(self._ptr, 0)
     elif opt_level == "speed":
         ffi.wasmtime_config_cranelift_opt_level_set(self._ptr, 1)
     elif opt_level == "speed_and_size":
         ffi.wasmtime_config_cranelift_opt_level_set(self._ptr, 2)
     else:
         raise WasmtimeError("unknown opt level: " + str(opt_level))
Example #16
0
 def __init__(self, valtype: ValType, mutable: bool):
     if mutable:
         mutability = ffi.WASM_VAR
     else:
         mutability = ffi.WASM_CONST
     type_ptr = take_owned_valtype(valtype)
     ptr = ffi.wasm_globaltype_new(type_ptr, mutability)
     if ptr == 0:
         raise WasmtimeError("failed to allocate GlobalType")
     self._ptr = ptr
     self._owner = None
Example #17
0
 def __init__(self, config: Config = None):
     if config is None:
         self._ptr = ffi.wasm_engine_new()
     elif not isinstance(config, Config):
         raise TypeError("expected Config")
     elif not hasattr(config, '_ptr'):
         raise WasmtimeError("Config already used")
     else:
         ptr = config._ptr
         delattr(config, '_ptr')
         self._ptr = ffi.wasm_engine_new_with_config(ptr)
Example #18
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 #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 __setitem__(self, idx: int, val: IntoVal) -> None:
        """
        Sets an individual element within this table.

        `idx` must be an integer index.

        The `val` specified must be convertible into this table's element
        type. I.e. for a `funcref` table, `val` must either be a `Func` or
        `None`, and for an `externref` table, `val` may be any arbitrary
        external data.

        Raises a `WasmtimeError` if `idx` is out of bounds.
        """
        if idx >= self.size:
            raise WasmtimeError(
                "Index out of bounds when setting table element")

        value = Val._convert(self.type.element, val)
        ok = ffi.wasm_table_set(self._ptr, idx, value._unwrap_raw().of.ref)
        if not ok:
            raise WasmtimeError("Failed to set table element")
Example #21
0
def invoke(idx, params_ptr, results_ptr, params):  # type: ignore
    func, param_tys, result_tys, store = FUNCTIONS.get(idx or 0)

    try:
        for i in range(0, len(param_tys)):
            params.append(Val._value(params_ptr[i]))
        results = func(*params)
        if len(result_tys) == 0:
            if results is not None:
                raise WasmtimeError(
                    "callback produced results when it shouldn't")
        elif len(result_tys) == 1:
            if isinstance(results, Val):
                # Because we are taking the inner value with `_into_raw`, we
                # need to ensure that we have a unique `Val`.
                val = results._clone()
            else:
                val = Val._convert(result_tys[0], results)
            results_ptr[0] = val._into_raw()
        else:
            if len(results) != len(result_tys):
                raise WasmtimeError(
                    "callback produced wrong number of results")
            for i, result in enumerate(results):
                # Because we are taking the inner value with `_into_raw`, we
                # need to ensure that we have a unique `Val`.
                if isinstance(result, Val):
                    val = result._clone()
                else:
                    val = Val._convert(result_tys[i], result)
                results_ptr[i] = val._into_raw()
    except Exception:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        fmt = traceback.format_exception(exc_type, exc_value, exc_traceback)
        trap = Trap(store, "\n".join(fmt))
        ptr = trap._ptr
        delattr(trap, '_ptr')
        return cast(ptr, c_void_p).value

    return 0
Example #22
0
    def fuel_consumed(self) -> int:
        """
        Returns the amount of fuel consumed by this `Store` so far.

        Raises a `WasmtimeError` if this store's configuration is not configured
        to consume fuel.
        """
        fuel = c_uint64(0)
        ok = ffi.wasmtime_store_fuel_consumed(self._ptr, byref(fuel))
        if ok:
            return fuel.value
        raise WasmtimeError(
            "fuel is not enabled in this store's configuration")
Example #23
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 #24
0
    def stdin_file(self, path: str) -> None:
        """
        Configures a file to be used as the stdin stream of this WASI
        configuration.

        Reads of the stdin stream will read the path specified.

        The file must already exist on the filesystem. If it cannot be
        opened then `WasmtimeError` is raised.
        """
        res = ffi.wasi_config_set_stdin_file(self._ptr,
                                             c_char_p(path.encode('utf-8')))
        if not res:
            raise WasmtimeError("failed to set stdin file")
Example #25
0
    def grow(self, delta: int) -> bool:
        """
        Grows this memory by the given number of pages
        """

        if not isinstance(delta, int):
            raise TypeError("expected an integer")
        if delta < 0:
            raise WasmtimeError("cannot grow by negative amount")
        ok = ffi.wasm_memory_grow(self._ptr, delta)
        if ok:
            return True
        else:
            return False
Example #26
0
    def __init__(self, store: Store, ty: MemoryType):
        """
        Creates a new memory in `store` with the given `ty`
        """

        if not isinstance(store, Store):
            raise TypeError("expected a Store")
        if not isinstance(ty, MemoryType):
            raise TypeError("expected a MemoryType")
        ptr = ffi.wasm_memory_new(store._ptr, ty._ptr)
        if not ptr:
            raise WasmtimeError("failed to create memory")
        self._ptr = ptr
        self._owner = None
Example #27
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 #28
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 #29
0
    def grow(self, amt: int, init: IntoVal) -> int:
        """
        Grows this table by the specified number of slots, using the specified
        initializer for all new table slots.

        Raises a `WasmtimeError` if the table could not be grown.
        Returns the previous size of the table otherwise.
        """
        init_val = Val._convert(self.type.element, init)
        ok = ffi.wasm_table_grow(self._ptr, c_uint32(amt),
                                 init_val._unwrap_raw().of.ref)
        if not ok:
            raise WasmtimeError("failed to grow table")
        return self.size - amt
Example #30
0
    def __init__(self, store: Store, message: str):
        """
        Creates a new trap in `store` with the given `message`
        """

        if not isinstance(store, Store):
            raise TypeError("expected a Store")
        if not isinstance(message, str):
            raise TypeError("expected a string")
        message_raw = ffi.str_to_name(message, trailing_nul=True)
        ptr = ffi.wasm_trap_new(store._ptr, byref(message_raw))
        if not ptr:
            raise WasmtimeError("failed to create trap")
        self._ptr = ptr