diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 3ded564..44fa953 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -74,4 +74,4 @@ jobs: - uses: actions/upload-artifact@v4 with: name: binaryen-py-sdist - path: dist/*.tar.gz \ No newline at end of file + path: dist/*.tar.gz diff --git a/binaryen/__init__.py b/binaryen/__init__.py index f0f2ace..7074aa1 100644 --- a/binaryen/__init__.py +++ b/binaryen/__init__.py @@ -3,4 +3,5 @@ from .__feature import Feature from .__functionref import FunctionRef from .__module import Module +from .__type_builder import TypeBuilder from ._binaryen import ffi, lib diff --git a/binaryen/__module.py b/binaryen/__module.py index b3457be..f57f1f7 100644 --- a/binaryen/__module.py +++ b/binaryen/__module.py @@ -7,7 +7,13 @@ from .__feature import Feature from .__functionref import FunctionRef from ._binaryen import ffi, lib -from .internals import BinaryenHeapType, BinaryenLiteral, BinaryenOp, BinaryenType +from .internals import ( + BinaryenGlobalRef, + BinaryenHeapType, + BinaryenLiteral, + BinaryenOp, + BinaryenType, +) from .type import TypeNone type BinaryenExportRef = Any @@ -170,7 +176,7 @@ def store( ptr.ref, value.ref, store_type, - memory_name, + _none_to_null(memory_name), ) return Expression(ref) @@ -226,7 +232,22 @@ def unreachable(self) -> Expression: # TODO: TupleMake, TupleExtract, Pop, RefI31, I31Get # TODO: CallRef, ReftTest, RefCast, BrOn # TODO: StructNew, StructGet, StructSet - # TODO: ArrayNew, ArrayNewData + + def array_new( + self, heap_type: BinaryenHeapType, size: Expression, init: Expression + ): + ref = lib.BinaryenArrayNew(self.ref, heap_type, size.ref, init.ref) + return Expression(ref) + + def array_new_data( + self, + heap_type: BinaryenHeapType, + name: bytes, + offset: Expression, + size: Expression, + ): + ref = lib.BinaryenArrayNewData(self.ref, heap_type, name, offset.ref, size.ref) + return Expression(ref) def array_new_fixed(self, heap_type: BinaryenHeapType, values: list[Expression]): num_values = len(values) @@ -234,7 +255,38 @@ def array_new_fixed(self, heap_type: BinaryenHeapType, values: list[Expression]) ref = lib.BinaryenArrayNewFixed(self.ref, heap_type, value_refs, num_values) return Expression(ref) - # TODO: ArrayGet, ArraySet, ArrayLen, ArrayGet, ArraySet, ArrayLen, ArrayCopy + def array_get( + self, ref: Expression, index: Expression, array_type: BinaryenType, signed: bool + ): + ref = lib.BinaryenArrayGet(self.ref, ref.ref, index.ref, array_type, signed) + return Expression(ref) + + def array_set(self, ref: Expression, index: Expression, value: Expression): + ref = lib.BinaryenArraySet(self.ref, ref.ref, index.ref, value.ref) + return Expression(ref) + + def array_len(self, ref: Expression): + ref = lib.BinaryenArrayLen(self.ref, ref.ref) + return Expression(ref) + + def array_copy( + self, + dest_ref: Expression, + dest_index: Expression, + src_ref: Expression, + src_index: Expression, + length: Expression, + ): + ref = lib.BinaryenArrayCopy( + self.ref, + dest_ref.ref, + dest_index.ref, + src_ref.ref, + src_index.ref, + length.ref, + ) + return Expression(ref) + # TODO: StringNew def string_const(self, name: bytes) -> Expression: @@ -331,7 +383,13 @@ def add_function_export( # TODO: GetExport, RemoveExport, GetNumExports, GetExportByIndex - # TODO: AddGlobal, GetGlobal, RemoveGlobal, GetNumGlobals, GetGlobalByIndex + def add_global( + self, name: bytes, global_type: BinaryenType, mutable: bool, init: Expression + ) -> BinaryenGlobalRef: + ref = lib.BinaryenAddGlobal(self.ref, name, global_type, mutable, init.ref) + return ref + + # TODO: GetGlobal, RemoveGlobal, GetNumGlobals, GetGlobalByIndex # TODO: AddTag, GetTag, RemoveTag @@ -344,7 +402,8 @@ def set_memory( initial: int, maximum: int, export_name: bytes, - segments: list[bytes], + segment_names: list[bytes], + segment_datas: list[bytes], segment_passive: list[bool], segment_offsets: list[Expression], segment_sizes: list[int], @@ -352,22 +411,30 @@ def set_memory( memory64: bool, name: bytes, ): - if len(segment_sizes) != len(segments): + if not ( + len(segment_names) + == len(segment_datas) + == len(segment_passive) + == len(segment_offsets) + == len(segment_sizes) + ): raise RuntimeError("Segment sizes do not match") segment_offset_refs = list(map(lambda x: x.ref, segment_offsets)) - segments_char_arr = list(map(lambda x: ffi.new("char[]", x), segments)) + segment_names_charptr = list(map(lambda x: ffi.new("char[]", x), segment_names)) + segment_datas_charptr = list(map(lambda x: ffi.new("char[]", x), segment_datas)) lib.BinaryenSetMemory( self.ref, initial, maximum, export_name, - segments_char_arr, + segment_names_charptr, + segment_datas_charptr, segment_passive, segment_offset_refs, segment_sizes, - len(segments), + len(segment_names), shared, memory64, name, @@ -451,5 +518,7 @@ def write_binary(self, filename: str): # TODO: SideEffects # TODO: CFG/Relooper # TODO: Expression Runner - # TODO: TypeBuilder + + # TODO: SetTypeName, SetFieldName + # TODO: Utilities diff --git a/binaryen/__type_builder.py b/binaryen/__type_builder.py new file mode 100644 index 0000000..08bcfad --- /dev/null +++ b/binaryen/__type_builder.py @@ -0,0 +1,76 @@ +from ._binaryen import lib, ffi +from .internals import ( + BinaryenExpressionId, + BinaryenType, + BinaryenPackedType, + BinaryenHeapType, +) + +type_builder_errors = { + lib.TypeBuilderErrorReasonSelfSupertype(): "Self Supertype", + lib.TypeBuilderErrorReasonInvalidSupertype(): "Invalid Supertype", + lib.TypeBuilderErrorReasonForwardSupertypeReference(): "Forward Supertype Reference", + lib.TypeBuilderErrorReasonForwardChildReference(): "Forward Child Reference", +} + + +class TypeBuilder: + def __init__(self, size: int): + self.ref = lib.TypeBuilderCreate(size) + + def grow(self, count: int): + if self.ref is None: + raise RuntimeError("Cannot access TypeBuilder after it has been built.") + lib.TypeBuilderGrow(self.ref, count) + + def get_size(self) -> int: + if self.ref is None: + raise RuntimeError("Cannot access TypeBuilder after it has been built.") + return lib.TypeBuilderGetSize(self.ref) + + def set_signature_type( + self, index: int, param_types: BinaryenType, result_types: BinaryenType + ): + if self.ref is None: + raise RuntimeError("Cannot access TypeBuilder after it has been built.") + lib.TypeBuilderSetSignatureType(self.ref, index, param_types, result_types) + + # TODO: SetStructType + + def set_array_type( + self, + index: int, + element_type: BinaryenType, + element_packed_type: BinaryenPackedType, + element_mutable: bool, + ): + if self.ref is None: + raise RuntimeError("Cannot access TypeBuilder after it has been built.") + lib.TypeBuilderSetArrayType( + self.ref, index, element_type, element_packed_type, element_mutable + ) + + # TODO: GetTempHeapType, GetTempTupleType, TempRefType, SetSubType, SetOpen, CreateRecGroup + + def build(self): + if self.ref is None: + raise RuntimeError("Cannot access TypeBuilder after it has been built.") + + size = self.get_size() + heap_types = ffi.new(f"BinaryenHeapType[{size}]") + error_index = ffi.new("BinaryenIndex[1]") + error_reason = ffi.new("TypeBuilderErrorReason[1]") + successful = lib.TypeBuilderBuildAndDispose( + self.ref, heap_types, error_index, error_reason + ) + self.ref = None + + if not successful: + raise RuntimeError( + f"TypeBuilder Error at type index {error_index[0]}: {type_builder_errors[error_reason[0]]}" + ) + + result: list[BinaryenHeapType] = [] + for i in range(size): + result.append(heap_types[i]) + return result diff --git a/binaryen/internals.py b/binaryen/internals.py index 1da821a..b527ad6 100644 --- a/binaryen/internals.py +++ b/binaryen/internals.py @@ -22,3 +22,4 @@ class __BaseType: BinaryenExternalKind = __NewType("BinaryenExternalKind", __BaseType) BinaryenOp = __NewType("BinaryenOp", __BaseType) BinaryenLiteral = __NewType("BinaryenLiteral", __BaseType) +BinaryenGlobalRef = __NewType("BinaryenGlobalRef", __BaseType) \ No newline at end of file diff --git a/examples/array.py b/examples/array.py new file mode 100644 index 0000000..588b273 --- /dev/null +++ b/examples/array.py @@ -0,0 +1,48 @@ +import binaryen +from binaryen.type import Int32, NotPacked + + +mod = binaryen.Module() +mod.set_feature(binaryen.Feature.GC | binaryen.Feature.ReferenceTypes) + +tb = binaryen.TypeBuilder(1) +tb.set_array_type(0, Int32, NotPacked, True) +Int32ArrayHeap = tb.build()[0] +Int32Array = binaryen.type.from_heap_type(Int32ArrayHeap, True) + +contents = mod.array_new(Int32ArrayHeap, mod.i32(10), mod.i32(0)) +set_array = mod.local_set(0, contents.copy(mod)) +array = mod.local_get(0, Int32Array) + +mod.add_function( + b"indexArray", + None, + Int32, + [Int32Array], + mod.block( + None, + [ + set_array, + array, + mod.Return(mod.array_len(array)) + ], + Int32, + ), +) + +mod.add_function_export(b"indexArray", b"indexArray") +mod.auto_drop() + +# mod.optimize() + +mod.print() +mod.write_text("array.wat") +mod.write_binary("array.wasm") + + +if not mod.validate(): + raise RuntimeError("Invalid module!") + +# Can either print with `myModule.print()` or write to file with `myModule.write_binary(__file__)` + +# Run the written binary with `wasmtime addTen.wasm --invoke addTen 12` diff --git a/examples/hello_world.py b/examples/hello_world.py index a2df4d8..1f88171 100644 --- a/examples/hello_world.py +++ b/examples/hello_world.py @@ -1,12 +1,12 @@ import binaryen as b -from binaryen.type import NULL, Int32, TypeNone +from binaryen.type import Int32, TypeNone mod = b.Module() mod.add_function_import( b"print", b"wasi_snapshot_preview1", b"fd_write", - b.types.create([Int32, Int32, Int32, Int32]), + b.type.create([Int32, Int32, Int32, Int32]), Int32, ) @@ -18,6 +18,7 @@ 1, 1, b"memory", + [b"str"], [b"Hello Wasm!\n"], [False], [mod.i32(8)], @@ -28,8 +29,8 @@ ) # Pointer to start of string and length of string -string_pointer = mod.store(4, 0, 0, mod.i32(0), mod.i32(8), Int32, NULL) -string_length = mod.store(4, 0, 2, mod.i32(4), mod.i32(12), Int32, NULL) +string_pointer = mod.store(4, 0, 0, mod.i32(0), mod.i32(8), Int32, None) +string_length = mod.store(4, 0, 2, mod.i32(4), mod.i32(12), Int32, None) # | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | # [ 8 ] [ 12 ] H e l l o \ W a s m ! \n \0 diff --git a/pyproject.toml b/pyproject.toml index a37e214..ff1e967 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,4 +61,4 @@ before-all = ["yum install wget -y", "bash ./scripts/build_libbinaryen.sh"] [[tool.cibuildwheel.overrides]] select = "*-musllinux*" -before-all = ["apk add wget", "bash ./scripts/build_libbinaryen.sh"] \ No newline at end of file +before-all = ["apk add wget", "bash ./scripts/build_libbinaryen.sh"]