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