Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wiring SharedMemory with it's corresponding bindings #255

Merged
merged 10 commits into from
Nov 25, 2024
56 changes: 56 additions & 0 deletions tests/test_shared_memory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import unittest

from wasmtime import *


class TestSharedMemory(unittest.TestCase):
def test_new(self):
engine = Store().engine
memory_type = MemoryType(Limits(1, 2), is_64=False, shared=True)
assert(not memory_type.is_64)
shared_memory = SharedMemory(engine, memory_type)
with self.assertRaises(TypeError):
shared_memory.grow('') # type: ignore
with self.assertRaises(WasmtimeError):
shared_memory.grow(-1)
self.assertEqual(shared_memory.data_ptr()[0], 0)
self.assertEqual(shared_memory.data_len(), 65536)
self.assertTrue(isinstance(shared_memory.type(), MemoryType))

def test_grow(self):
engine = Store().engine
memory_type = MemoryType(Limits(1, 2), shared=True)
shared_memory = SharedMemory(engine, memory_type)
assert(shared_memory.grow(1) == 1)
assert(shared_memory.grow(0) == 2)
with self.assertRaises(WasmtimeError):
shared_memory.grow(1)

def test_errors(self):
engine = Store().engine
ty = MemoryType(Limits(1, 2), shared=True)
with self.assertRaises(AttributeError):
SharedMemory(1, ty) # type: ignore
with self.assertRaises(AttributeError):
SharedMemory(engine, 1) # type: ignore

def test_shared_memory_type_fails_if_no_max_specified(self):
with self.assertRaises(WasmtimeError):
# Shared memories must have a max size
MemoryType(Limits(0x100000000, None), shared=True)

def test_shared_memory_type_works_if_min_max_i64_is_set(self):
ty = MemoryType(Limits(0x100000000, 0x100000000), is_64=True, shared=True)
assert(ty.limits.min == 0x100000000)
assert(ty.limits.max == 0x100000000)
assert(ty.is_64)

def test_shared_memory_type_fails_if_is_too_large(self):
with self.assertRaises(WasmtimeError):
MemoryType(Limits(1, 0x100000000), shared=True)

def test_memory_type_has_to_be_shared(self):
engine = Store().engine
ty = MemoryType(Limits(1, 2))
with self.assertRaises(WasmtimeError):
SharedMemory(engine, ty)
2 changes: 2 additions & 0 deletions wasmtime/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from ._instance import Instance
from ._wasi import WasiConfig, FilePerms, DirPerms
from ._linker import Linker
from ._sharedmemory import SharedMemory

__all__ = [
'wat2wasm',
Expand All @@ -50,6 +51,7 @@
'Caller',
'Table',
'Memory',
'SharedMemory',
'Global',
'Trap',
'TrapCode',
Expand Down
3 changes: 2 additions & 1 deletion wasmtime/_exportable.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
from ._func import Func
from ._globals import Global
from ._memory import Memory
from ._sharedmemory import SharedMemory
from ._table import Table
from ._module import Module
from ._instance import Instance

AsExtern = typing.Union["Func", "Table", "Memory", "Global", "Instance", "Module"]
AsExtern = typing.Union["Func", "Table", "Memory", "SharedMemory", "Global", "Instance", "Module"]
8 changes: 6 additions & 2 deletions wasmtime/_extern.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


def wrap_extern(ptr: ffi.wasmtime_extern_t) -> AsExtern:
from wasmtime import Func, Table, Global, Memory, Module, Instance
from wasmtime import Func, Table, Global, Memory, SharedMemory, Module, Instance

if ptr.kind == ffi.WASMTIME_EXTERN_FUNC.value:
return Func._from_raw(ptr.of.func)
Expand All @@ -15,6 +15,8 @@ def wrap_extern(ptr: ffi.wasmtime_extern_t) -> AsExtern:
return Global._from_raw(ptr.of.global_)
if ptr.kind == ffi.WASMTIME_EXTERN_MEMORY.value:
return Memory._from_raw(ptr.of.memory)
if ptr.kind == ffi.WASMTIME_EXTERN_SHAREDMEMORY.value:
return SharedMemory._from_ptr(ptr.of.sharedmemory)
if ptr.kind == ffi.WASMTIME_EXTERN_INSTANCE.value:
return Instance._from_raw(ptr.of.instance)
if ptr.kind == ffi.WASMTIME_EXTERN_MODULE.value:
Expand All @@ -23,14 +25,16 @@ def wrap_extern(ptr: ffi.wasmtime_extern_t) -> AsExtern:


def get_extern_ptr(item: AsExtern) -> ffi.wasmtime_extern_t:
from wasmtime import Func, Table, Global, Memory, Module, Instance
from wasmtime import Func, Table, Global, Memory, SharedMemory, Module, Instance

if isinstance(item, Func):
return item._as_extern()
elif isinstance(item, Global):
return item._as_extern()
elif isinstance(item, Memory):
return item._as_extern()
elif isinstance(item, SharedMemory):
return item._as_extern()
elif isinstance(item, Table):
return item._as_extern()
elif isinstance(item, Module):
Expand Down
1 change: 1 addition & 0 deletions wasmtime/_ffi.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
WASMTIME_EXTERN_GLOBAL = c_uint8(1)
WASMTIME_EXTERN_TABLE = c_uint8(2)
WASMTIME_EXTERN_MEMORY = c_uint8(3)
WASMTIME_EXTERN_SHAREDMEMORY = c_uint8(4)
atilag marked this conversation as resolved.
Show resolved Hide resolved
WASMTIME_EXTERN_INSTANCE = c_uint8(4)
WASMTIME_EXTERN_MODULE = c_uint8(5)

Expand Down
80 changes: 80 additions & 0 deletions wasmtime/_sharedmemory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from . import _ffi as ffi
from ctypes import *
import ctypes
from typing import Optional, Any
from wasmtime import MemoryType, WasmtimeError, Engine, Managed
from ._store import Storelike



class SharedMemory(Managed["ctypes._Pointer[ffi.wasmtime_sharedmemory_t]"]):
def __init__(self, engine: Engine, ty: MemoryType):
"""
Creates a new shared memory in `store` with the given `ty`
"""

sharedmemory_ptr = POINTER(ffi.wasmtime_sharedmemory_t)()
error = ffi.wasmtime_sharedmemory_new(engine.ptr(), ty.ptr(), byref(sharedmemory_ptr))
if error:
raise WasmtimeError._from_ptr(error)
self._set_ptr(sharedmemory_ptr)

def _delete(self, ptr: "ctypes._Pointer[ffi.wasmtime_sharedmemory_t]") -> None:
ffi.wasmtime_sharedmemory_delete(ptr)

@classmethod
def _from_ptr(cls, ptr: "ctypes._Pointer[ffi.wasmtime_sharedmemory_t]") -> "SharedMemory":
if not isinstance(ptr, POINTER(ffi.wasmtime_sharedmemory_t)):
raise TypeError("wrong shared memory pointer type provided to _from_ptr")

ty: "SharedMemory" = cls.__new__(cls)
ty._set_ptr(ptr)
return ty

def type(self) -> MemoryType:
"""
Gets the type of this memory as a `MemoryType`
"""

ptr = ffi.wasmtime_sharedmemory_type(self.ptr())
return MemoryType._from_ptr(ptr, None)

def grow(self, delta: int) -> int:
"""
Grows this memory by the given number of pages
"""

if delta < 0:
raise WasmtimeError("cannot grow by negative amount")
prev = ffi.c_uint64(0)
error = ffi.wasmtime_sharedmemory_grow(self.ptr(), delta, byref(prev))
if error:
raise WasmtimeError._from_ptr(error)
return prev.value

def size(self) -> int:
"""
Returns the size, in WebAssembly pages, of this shared memory.
"""

return ffi.wasmtime_sharedmemory_size(byref(self.ptr()))

def data_ptr(self) -> "ctypes._Pointer[c_ubyte]":
"""
Returns the raw pointer in memory where this wasm shared memory lives.

Remember that all accesses to wasm shared memory should be bounds-checked
against the `data_len` method.
"""
return ffi.wasmtime_sharedmemory_data(self.ptr())

def data_len(self) -> int:
"""
Returns the raw byte length of this memory.
"""

return ffi.wasmtime_sharedmemory_data_size(self.ptr())

def _as_extern(self) -> ffi.wasmtime_extern_t:
union = ffi.wasmtime_extern_union(sharedmemory=pointer(self.ptr()))
return ffi.wasmtime_extern_t(ffi.WASMTIME_EXTERN_SHAREDMEMORY, union)
10 changes: 9 additions & 1 deletion wasmtime/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,18 @@ def is_64(self) -> bool:
Returns whether or not this is a 64-bit memory
"""
return ffi.wasmtime_memorytype_is64(self.ptr())

@property
def is_shared(self) -> bool:
"""
Returns whether or not this is a shared memory
"""
return ffi.wasmtime_memorytype_isshared(self.ptr())


def _as_extern(self) -> "ctypes._Pointer[ffi.wasm_externtype_t]":
return ffi.wasm_memorytype_as_externtype_const(self.ptr())


def wrap_externtype(ptr: "ctypes._Pointer[ffi.wasm_externtype_t]", owner: Optional[Any]) -> "AsExternType":
if not isinstance(ptr, POINTER(ffi.wasm_externtype_t)):
Expand Down
Loading