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
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)
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
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
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")
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
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
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
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
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
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)
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)
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)
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
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))
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
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)
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)
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)
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")
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
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")
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
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")
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
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
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)
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)
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
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