From 838456e5b78d1544c287b6c7c6b5f61b6a330a6b Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Sat, 6 Dec 2025 16:11:16 -0500 Subject: [PATCH 01/11] feat: Implement test framework and `module` suite --- binaryen.opam | 1 + esy.lock/index.json | 137 ++++++++++++-- esy.lock/opam/cppo.1.8.0/opam | 49 +++++ esy.lock/opam/merlin-extend.0.6.2/opam | 30 ++++ esy.lock/opam/ocamlfind.1.9.8/opam | 2 +- esy.lock/opam/reason.3.17.2/opam | 51 ++++++ package.json | 3 +- test/framework.re | 8 + test/framework.rei | 2 + test/module.re | 165 +++++++++++++++++ test/test.expected | 239 ++++++------------------- test/test.ml | 5 +- 12 files changed, 489 insertions(+), 203 deletions(-) create mode 100644 esy.lock/opam/cppo.1.8.0/opam create mode 100644 esy.lock/opam/merlin-extend.0.6.2/opam create mode 100644 esy.lock/opam/reason.3.17.2/opam create mode 100644 test/framework.re create mode 100644 test/framework.rei create mode 100644 test/module.re diff --git a/binaryen.opam b/binaryen.opam index 0591f426..6fc80527 100644 --- a/binaryen.opam +++ b/binaryen.opam @@ -17,5 +17,6 @@ depends: [ "dune-configurator" {>= "3.0.0"} "js_of_ocaml-compiler" {>= "6.0.0" < "7.0.0"} "libbinaryen" {>= "124.0.1" < "125.0.0"} + "reason" {>= "3.11.0" <= "3.17.2"} ] x-maintenance-intent: ["0.(latest)"] diff --git a/esy.lock/index.json b/esy.lock/index.json index 637fbaaf..87045eb2 100644 --- a/esy.lock/index.json +++ b/esy.lock/index.json @@ -1,5 +1,5 @@ { - "checksum": "43b607828957b76c0263dc0b329d0237", + "checksum": "5e3444b0bf9652970a0970ab93cbdaf0", "root": "@grain/binaryen.ml@link-dev:./package.json", "node": { "ocaml@5.3.0@d41d8cd9": { @@ -103,7 +103,7 @@ "overrides": [], "dependencies": [ "ocaml@5.3.0@d41d8cd9", "@opam/topkg@opam:1.1.1@2377d2f8", - "@opam/ocamlfind@opam:1.9.8@ee910ff5", + "@opam/ocamlfind@opam:1.9.8@4b291364", "@opam/ocamlbuild@opam:0.16.1@b3fc8209", "@opam/cmdliner@opam:1.3.0@8e6dd99f", "@esy-ocaml/substs@0.0.1@d41d8cd9" @@ -136,7 +136,7 @@ "dependencies": [ "ocaml@5.3.0@d41d8cd9", "@opam/uutf@opam:1.0.4@ba7fbef7", "@opam/uucp@opam:17.0.0@843de755", "@opam/topkg@opam:1.1.1@2377d2f8", - "@opam/ocamlfind@opam:1.9.8@ee910ff5", + "@opam/ocamlfind@opam:1.9.8@4b291364", "@opam/ocamlbuild@opam:0.16.1@b3fc8209", "@opam/cmdliner@opam:1.3.0@8e6dd99f", "@esy-ocaml/substs@0.0.1@d41d8cd9" @@ -170,7 +170,7 @@ "overrides": [], "dependencies": [ "ocaml@5.3.0@d41d8cd9", "@opam/topkg@opam:1.1.1@2377d2f8", - "@opam/ocamlfind@opam:1.9.8@ee910ff5", + "@opam/ocamlfind@opam:1.9.8@4b291364", "@opam/ocamlbuild@opam:0.16.1@b3fc8209", "@opam/cmdliner@opam:1.3.0@8e6dd99f", "@esy-ocaml/substs@0.0.1@d41d8cd9" @@ -201,7 +201,7 @@ }, "overrides": [], "dependencies": [ - "ocaml@5.3.0@d41d8cd9", "@opam/ocamlfind@opam:1.9.8@ee910ff5", + "ocaml@5.3.0@d41d8cd9", "@opam/ocamlfind@opam:1.9.8@4b291364", "@opam/ocamlbuild@opam:0.16.1@b3fc8209", "@esy-ocaml/substs@0.0.1@d41d8cd9" ], @@ -450,6 +450,50 @@ [ "windows", "x86_64" ] ] }, + "@opam/reason@opam:3.17.2@2ea991b1": { + "id": "@opam/reason@opam:3.17.2@2ea991b1", + "name": "@opam/reason", + "version": "opam:3.17.2", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/sha256/7f/7f4087016e8c393a13d57b7737677bc1dbf16978fd43c19354d4d79a794c8c47#sha256:7f4087016e8c393a13d57b7737677bc1dbf16978fd43c19354d4d79a794c8c47", + "archive:https://github.com/reasonml/reason/releases/download/3.17.2/reason-3.17.2.tbz#sha256:7f4087016e8c393a13d57b7737677bc1dbf16978fd43c19354d4d79a794c8c47" + ], + "opam": { + "name": "reason", + "version": "3.17.2", + "path": "esy.lock/opam/reason.3.17.2" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@5.3.0@d41d8cd9", "@opam/ppxlib@opam:0.37.0@42a12c9c", + "@opam/ocamlfind@opam:1.9.8@4b291364", + "@opam/merlin-extend@opam:0.6.2@f9530fc0", + "@opam/menhir@opam:20250912@0ed10637", + "@opam/fix@opam:20250919@2cb92ccc", + "@opam/dune-build-info@opam:3.20.2@a53d0f1c", + "@opam/dune@opam:3.20.2@8daef28d", "@opam/cppo@opam:1.8.0@b48266ea", + "@opam/cmdliner@opam:1.3.0@8e6dd99f", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@5.3.0@d41d8cd9", "@opam/ppxlib@opam:0.37.0@42a12c9c", + "@opam/merlin-extend@opam:0.6.2@f9530fc0", + "@opam/menhir@opam:20250912@0ed10637", + "@opam/fix@opam:20250919@2cb92ccc", + "@opam/dune-build-info@opam:3.20.2@a53d0f1c", + "@opam/dune@opam:3.20.2@8daef28d", "@opam/cppo@opam:1.8.0@b48266ea", + "@opam/cmdliner@opam:1.3.0@8e6dd99f" + ], + "available": [ + [ "darwin", "x86_64" ], + [ "darwin", "arm64" ], + [ "linux", "x86_64" ], + [ "windows", "x86_64" ] + ] + }, "@opam/re@opam:1.14.0@62aa9f42": { "id": "@opam/re@opam:1.14.0@62aa9f42", "name": "@opam/re", @@ -662,14 +706,14 @@ }, "overrides": [], "dependencies": [ - "ocaml@5.3.0@d41d8cd9", "@opam/ocamlfind@opam:1.9.8@ee910ff5", + "ocaml@5.3.0@d41d8cd9", "@opam/ocamlfind@opam:1.9.8@4b291364", "@opam/dune@opam:3.20.2@8daef28d", "@opam/cmdliner@opam:1.3.0@8e6dd99f", "@opam/base-bytes@opam:base@785dbd33", "@esy-ocaml/substs@0.0.1@d41d8cd9" ], "devDependencies": [ - "ocaml@5.3.0@d41d8cd9", "@opam/ocamlfind@opam:1.9.8@ee910ff5", + "ocaml@5.3.0@d41d8cd9", "@opam/ocamlfind@opam:1.9.8@4b291364", "@opam/dune@opam:3.20.2@8daef28d", "@opam/cmdliner@opam:1.3.0@8e6dd99f", "@opam/base-bytes@opam:base@785dbd33" @@ -813,8 +857,8 @@ [ "windows", "x86_64" ] ] }, - "@opam/ocamlfind@opam:1.9.8@ee910ff5": { - "id": "@opam/ocamlfind@opam:1.9.8@ee910ff5", + "@opam/ocamlfind@opam:1.9.8@4b291364": { + "id": "@opam/ocamlfind@opam:1.9.8@4b291364", "name": "@opam/ocamlfind", "version": "opam:1.9.8", "source": { @@ -1099,6 +1143,37 @@ [ "windows", "x86_64" ] ] }, + "@opam/merlin-extend@opam:0.6.2@f9530fc0": { + "id": "@opam/merlin-extend@opam:0.6.2@f9530fc0", + "name": "@opam/merlin-extend", + "version": "opam:0.6.2", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/sha256/47/47558e7f30b64462f2b9c82fb7f787133acfa0d5132452b6ad7848e0b0f4d779#sha256:47558e7f30b64462f2b9c82fb7f787133acfa0d5132452b6ad7848e0b0f4d779", + "archive:https://github.com/let-def/merlin-extend/releases/download/v0.6.2/merlin-extend-0.6.2.tbz#sha256:47558e7f30b64462f2b9c82fb7f787133acfa0d5132452b6ad7848e0b0f4d779" + ], + "opam": { + "name": "merlin-extend", + "version": "0.6.2", + "path": "esy.lock/opam/merlin-extend.0.6.2" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@5.3.0@d41d8cd9", "@opam/dune@opam:3.20.2@8daef28d", + "@opam/cppo@opam:1.8.0@b48266ea", "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@5.3.0@d41d8cd9", "@opam/dune@opam:3.20.2@8daef28d" + ], + "available": [ + [ "darwin", "x86_64" ], + [ "darwin", "arm64" ], + [ "linux", "x86_64" ], + [ "windows", "x86_64" ] + ] + }, "@opam/menhirSdk@opam:20250912@f434747d": { "id": "@opam/menhirSdk@opam:20250912@f434747d", "name": "@opam/menhirSdk", @@ -1319,7 +1394,7 @@ "ocaml@5.3.0@d41d8cd9", "@opam/yojson@opam:3.0.0@b2c9a6c1", "@opam/sedlex@opam:3.7@7fb2caab", "@opam/ppxlib@opam:0.37.0@42a12c9c", - "@opam/ocamlfind@opam:1.9.8@ee910ff5", + "@opam/ocamlfind@opam:1.9.8@4b291364", "@opam/menhirSdk@opam:20250912@f434747d", "@opam/menhirLib@opam:20250912@7bcb2f61", "@opam/menhir@opam:20250912@0ed10637", @@ -1395,7 +1470,7 @@ "overrides": [], "dependencies": [ "ocaml@5.3.0@d41d8cd9", "@opam/topkg@opam:1.1.1@2377d2f8", - "@opam/ocamlfind@opam:1.9.8@ee910ff5", + "@opam/ocamlfind@opam:1.9.8@4b291364", "@opam/ocamlbuild@opam:0.16.1@b3fc8209", "@opam/astring@opam:0.8.5@9975798d", "@esy-ocaml/substs@0.0.1@d41d8cd9" @@ -1709,6 +1784,39 @@ [ "windows", "x86_64" ] ] }, + "@opam/cppo@opam:1.8.0@b48266ea": { + "id": "@opam/cppo@opam:1.8.0@b48266ea", + "name": "@opam/cppo", + "version": "opam:1.8.0", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/a1/a197cb393b84f6b30e0ff55080ac429b#md5:a197cb393b84f6b30e0ff55080ac429b", + "archive:https://github.com/ocaml-community/cppo/archive/v1.8.0.tar.gz#md5:a197cb393b84f6b30e0ff55080ac429b" + ], + "opam": { + "name": "cppo", + "version": "1.8.0", + "path": "esy.lock/opam/cppo.1.8.0" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@5.3.0@d41d8cd9", "@opam/dune@opam:3.20.2@8daef28d", + "@opam/base-unix@opam:base@87d0b2eb", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@5.3.0@d41d8cd9", "@opam/dune@opam:3.20.2@8daef28d", + "@opam/base-unix@opam:base@87d0b2eb" + ], + "available": [ + [ "darwin", "x86_64" ], + [ "darwin", "arm64" ], + [ "linux", "x86_64" ], + [ "windows", "x86_64" ] + ] + }, "@opam/conf-cmake@github:grain-lang/cmake:esy.json#033cab656dc71a6488b3c1ca6ea45099f794bd03@d41d8cd9": { "id": "@opam/conf-cmake@github:grain-lang/cmake:esy.json#033cab656dc71a6488b3c1ca6ea45099f794bd03@d41d8cd9", "name": "@opam/conf-cmake", @@ -1880,11 +1988,11 @@ }, "overrides": [], "dependencies": [ - "ocaml@5.3.0@d41d8cd9", "@opam/ocamlfind@opam:1.9.8@ee910ff5", + "ocaml@5.3.0@d41d8cd9", "@opam/ocamlfind@opam:1.9.8@4b291364", "@esy-ocaml/substs@0.0.1@d41d8cd9" ], "devDependencies": [ - "ocaml@5.3.0@d41d8cd9", "@opam/ocamlfind@opam:1.9.8@ee910ff5" + "ocaml@5.3.0@d41d8cd9", "@opam/ocamlfind@opam:1.9.8@4b291364" ], "available": [ [ "darwin", "x86_64" ], @@ -1948,7 +2056,7 @@ "overrides": [], "dependencies": [ "ocaml@5.3.0@d41d8cd9", "@opam/topkg@opam:1.1.1@2377d2f8", - "@opam/ocamlfind@opam:1.9.8@ee910ff5", + "@opam/ocamlfind@opam:1.9.8@4b291364", "@opam/ocamlbuild@opam:0.16.1@b3fc8209", "@esy-ocaml/substs@0.0.1@d41d8cd9" ], @@ -2002,6 +2110,7 @@ "@grain/libbinaryen@124.0.1@d41d8cd9" ], "devDependencies": [ + "@opam/reason@opam:3.17.2@2ea991b1", "@opam/ocamlformat@opam:0.27.0@c40d4612", "@opam/ocaml-lsp-server@opam:1.23.1@38cf22a2", "@opam/js_of_ocaml-compiler@opam:6.2.0@45dd83ba" diff --git a/esy.lock/opam/cppo.1.8.0/opam b/esy.lock/opam/cppo.1.8.0/opam new file mode 100644 index 00000000..85270a79 --- /dev/null +++ b/esy.lock/opam/cppo.1.8.0/opam @@ -0,0 +1,49 @@ +# This file is generated by dune, edit dune-project instead +opam-version: "2.0" +synopsis: "Code preprocessor like cpp for OCaml" +description: """ +Cppo is an equivalent of the C preprocessor for OCaml programs. +It allows the definition of simple macros and file inclusion. + +Cppo is: + +* more OCaml-friendly than cpp +* easy to learn without consulting a manual +* reasonably fast +* simple to install and to maintain +""" +maintainer: [ + "Martin Jambon " "Yishuai Li " +] +authors: ["Martin Jambon"] +license: "BSD-3-Clause" +homepage: "https://github.com/ocaml-community/cppo" +doc: "https://ocaml-community.github.io/cppo" +bug-reports: "https://github.com/ocaml-community/cppo/issues" +depends: [ + "ocaml" {>= "4.02.3"} + "dune" {>= "2.0"} + "base-unix" +] +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] +dev-repo: "git+https://github.com/ocaml-community/cppo.git" +url { + src: "https://github.com/ocaml-community/cppo/archive/v1.8.0.tar.gz" + checksum: [ + "md5=a197cb393b84f6b30e0ff55080ac429b" + "sha512=3840725b767a0300bdc48f11d26d798bdcae0a764ed6798df3a08dfc8cc76fe124b14a19d47c9b5ea8e229d68b0311510afce77c0e4d9131fbda5116dc2689a2" + ] +} diff --git a/esy.lock/opam/merlin-extend.0.6.2/opam b/esy.lock/opam/merlin-extend.0.6.2/opam new file mode 100644 index 00000000..dbef263d --- /dev/null +++ b/esy.lock/opam/merlin-extend.0.6.2/opam @@ -0,0 +1,30 @@ +opam-version: "2.0" +maintainer: "Frederic Bour " +authors: "Frederic Bour " +homepage: "https://github.com/let-def/merlin-extend" +bug-reports: "https://github.com/let-def/merlin-extend" +license: "MIT" +dev-repo: "git+https://github.com/let-def/merlin-extend.git" +build: [ + ["dune" "subst"] {dev} + ["dune" "build" "-p" name "-j" jobs] +] +depends: [ + "dune" {>= "1.0"} + "cppo" {build & >= "1.1.0"} + "ocaml" {>= "4.02.3"} +] +synopsis: "A protocol to provide custom frontend to Merlin" +description: """ +This protocol allows to replace the OCaml frontend of Merlin. +It extends what used to be done with the `-pp' flag to handle a few more cases.""" +doc: "https://let-def.github.io/merlin-extend" +url { + src: + "https://github.com/let-def/merlin-extend/releases/download/v0.6.2/merlin-extend-0.6.2.tbz" + checksum: [ + "sha256=47558e7f30b64462f2b9c82fb7f787133acfa0d5132452b6ad7848e0b0f4d779" + "sha512=50696cb2099b84d4a5497fb778c969ca446e5639a91bcde6e2177588fbf72fe4f7a3c27b62384292ad873291719c5893673f1acce4755e81b5e05f9fd3e45b65" + ] +} +x-commit-hash: "098988ee19502645cf039b41027ec4f5e89197ab" diff --git a/esy.lock/opam/ocamlfind.1.9.8/opam b/esy.lock/opam/ocamlfind.1.9.8/opam index d9fe3931..8fb38dc5 100644 --- a/esy.lock/opam/ocamlfind.1.9.8/opam +++ b/esy.lock/opam/ocamlfind.1.9.8/opam @@ -12,7 +12,7 @@ license: "MIT" homepage: "http://projects.camlcity.org/projects/findlib.html" bug-reports: "https://github.com/ocaml/ocamlfind/issues" depends: [ - "ocaml" {>= "3.08.0"} + "ocaml" {>= "3.08.0" & (os != "cygwin" & os-distribution != "msys2" | < "5.0")} ] depopts: ["graphics"] build: [ diff --git a/esy.lock/opam/reason.3.17.2/opam b/esy.lock/opam/reason.3.17.2/opam new file mode 100644 index 00000000..19fd44ea --- /dev/null +++ b/esy.lock/opam/reason.3.17.2/opam @@ -0,0 +1,51 @@ +opam-version: "2.0" +synopsis: "Reason: Syntax & Toolchain for OCaml" +description: """ +Reason gives OCaml a new syntax that is remniscient of languages like +JavaScript. It's also the umbrella project for a set of tools for the OCaml & +JavaScript ecosystem.""" +maintainer: [ + "Jordan Walke " + "Antonio Nuno Monteiro " +] +authors: ["Jordan Walke "] +license: "MIT" +homepage: "https://reasonml.github.io/" +bug-reports: "https://github.com/reasonml/reason/issues" +depends: [ + "dune" {>= "3.18"} + "ocaml" {>= "4.08" & < "5.5"} + "ocamlfind" {build} + "cmdliner" {>= "1.1.0" & < "2.0"} + "dune-build-info" {>= "2.9.3"} + "menhir" {>= "20180523"} + "merlin-extend" {>= "0.6.2"} + "fix" + "cppo" + "ppxlib" {>= "0.36"} + "odoc" {with-doc} +] +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@doc" {with-doc} + ] +] +dev-repo: "git+https://github.com/reasonml/reason.git" +x-maintenance-intent: ["(latest)"] +url { + src: + "https://github.com/reasonml/reason/releases/download/3.17.2/reason-3.17.2.tbz" + checksum: [ + "sha256=7f4087016e8c393a13d57b7737677bc1dbf16978fd43c19354d4d79a794c8c47" + "sha512=3d3015d25bce329fbee9cef9b114831e7db1c10e19a26cbcaa27948c376bff3518b9d56fda9091c6df887df9ee8c33f45e1071e87933de79d65bcbf58c93cb17" + ] +} +x-commit-hash: "fccd05cb485e6c1f2fd35c3115d6e08b17024472" diff --git a/package.json b/package.json index b0727de4..8267c462 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "devDependencies": { "@opam/js_of_ocaml-compiler": ">= 6.0.0 < 7.0.0", "@opam/ocamlformat": "0.27.0", - "@opam/ocaml-lsp-server": ">= 1.9.1 < 2.0.0" + "@opam/ocaml-lsp-server": ">= 1.9.1 < 2.0.0", + "@opam/reason": ">= 3.11.0 <= 3.17.2" }, "resolutions": { "@opam/ocp-indent": "1.7.0" diff --git a/test/framework.re b/test/framework.re new file mode 100644 index 00000000..31002745 --- /dev/null +++ b/test/framework.re @@ -0,0 +1,8 @@ +let suite = (name, suiteFn) => { + Printf.printf("Running suite: %s\n", name); + suiteFn(); +}; +let test = (name, fn) => { + Printf.printf(". Running test: %s\n", name); + fn(); +}; diff --git a/test/framework.rei b/test/framework.rei new file mode 100644 index 00000000..4cb6c977 --- /dev/null +++ b/test/framework.rei @@ -0,0 +1,2 @@ +let suite: (string, unit => unit) => unit; +let test: (string, unit => unit) => unit; diff --git a/test/module.re b/test/module.re new file mode 100644 index 00000000..d6f313d5 --- /dev/null +++ b/test/module.re @@ -0,0 +1,165 @@ +open Binaryen; +open Framework; + +suite("Module", () => { + let create_test_func = wasm_mod => { + let _ = + Function.add_function( + wasm_mod, + "test_func", + Type.none, + Type.none, + [||], + Expression.Block.make( + wasm_mod, + ~return_type=Type.none, + "test_body", + [ + Expression.Drop.make( + wasm_mod, + Expression.Const.make(wasm_mod, Literal.int32(0l)), + ), + ], + ), + ); + (); + }; + test("create and dispose", () => { + let wasm_mod = Module.create(); + Module.dispose(wasm_mod); + }); + test("add custom section", () => { + let wasm_mod = Module.create(); + Module.add_custom_section(wasm_mod, "test_section", "test_data"); + Module.dispose(wasm_mod); + }); + test("write", () => { + let wasm_mod = Module.create(); + let (bytes, source_map) = Module.write(wasm_mod, None); + assert(source_map == None); + assert(bytes == Bytes.of_string("\000asm\001\000\000\000")); + let (_, source_map) = Module.write(wasm_mod, Some("")); + assert( + source_map + == Some({|{"version":3,"sources":[],"names":[],"mappings":""}|}), + ); + Module.dispose(wasm_mod); + }); + test("write_text", () => { + let wasm_mod = Module.create(); + create_test_func(wasm_mod); + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("write_stack_ir", () => { + let wasm_mod = Module.create(); + create_test_func(wasm_mod); + Printf.printf("%s", Module.write_stack_ir(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("parse", () => { + let wasm_bytes = Bytes.of_string("(module (func))"); + let wasm_mod = Module.parse(Bytes.to_string(wasm_bytes)); + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); + + // Note: Print tests will output to stdout directly. + test("print", () => { + let wasm_mod = Module.create(); + Module.print(wasm_mod); + Module.dispose(wasm_mod); + }); + test("print_asmjs", () => { + let wasm_mod = Module.create(); + create_test_func(wasm_mod); + Module.print_asmjs(wasm_mod); + Module.dispose(wasm_mod); + }); + test("print_stack_ir", () => { + let wasm_mod = Module.create(); + create_test_func(wasm_mod); + Module.print_stack_ir(wasm_mod); + Module.dispose(wasm_mod); + }); + + test("validate", () => { + let wasm_mod = Module.create(); + let result = Module.validate(wasm_mod); + assert(result == 1); + Module.dispose(wasm_mod); + }); + test("optimize", () => { + let wasm_mod = Module.create(); + Module.optimize(wasm_mod); + Module.dispose(wasm_mod); + }); + test("get and set features", () => { + let wasm_mod = Module.create(); + assert(Module.get_features(wasm_mod) == []); + Module.set_features( + wasm_mod, + [ + Module.Feature.mvp, + Module.Feature.atomics, + Module.Feature.mutable_globals, + Module.Feature.nontrapping_fp_to_int, + Module.Feature.simd128, + Module.Feature.bulk_memory, + Module.Feature.sign_ext, + Module.Feature.exception_handling, + Module.Feature.tail_call, + Module.Feature.reference_types, + Module.Feature.multivalue, + Module.Feature.gc, + Module.Feature.memory64, + Module.Feature.relaxed_simd, + Module.Feature.extended_const, + Module.Feature.strings, + Module.Feature.multi_memory, + Module.Feature.stack_switching, + Module.Feature.shared_everything, + Module.Feature.fp16, + Module.Feature.bulk_memory_opt, + Module.Feature.call_indirect_overlong, + Module.Feature.all, + ], + ); + assert(List.length(Module.get_features(wasm_mod)) == 22); + Module.dispose(wasm_mod); + }); + test("run_empty_passes", () => { + let wasm_mod = Module.create(); + Module.run_passes(wasm_mod, []); + Module.dispose(wasm_mod); + }); + test("read", () => { + let wasm_mod = Module.create(); + create_test_func(wasm_mod); + let (bytes, _) = Module.write(wasm_mod, None); + Module.dispose(wasm_mod); + let wasm_mod = Module.read(bytes); + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + let wasm_bytes = Bytes.of_string("\000asm\001\000\000\000"); + let wasm_mod = Module.read(wasm_bytes); + Module.dispose(wasm_mod); + }); + test("interpret", () => { + let wasm_mod = Module.create(); + Module.interpret(wasm_mod); + Module.dispose(wasm_mod); + }); + test("debug info filename management", () => { + let wasm_mod = Module.create(); + let index = Module.add_debug_info_filename(wasm_mod, "debug_file.wasm"); + let filename = Module.get_debug_info_filename(wasm_mod, index); + assert(filename == "debug_file.wasm"); + Module.dispose(wasm_mod); + }); + test("update maps", () => { + let wasm_mod = Module.create(); + Module.update_maps(wasm_mod); + Module.dispose(wasm_mod); + }); +}); diff --git a/test/test.expected b/test/test.expected index 8e1c7db2..2339df93 100644 --- a/test/test.expected +++ b/test/test.expected @@ -1,207 +1,76 @@ -(table.get $table - (i32.const 0) -) -(table.size $table) -(table.grow $table - (ref.null nofunc) - (i32.const 0) +(module ) + +function asmFunc(imports) { + var Math_imul = Math.imul; + var Math_fround = Math.fround; + var Math_abs = Math.abs; + var Math_clz32 = Math.clz32; + var Math_min = Math.min; + var Math_max = Math.max; + var Math_floor = Math.floor; + var Math_ceil = Math.ceil; + var Math_trunc = Math.trunc; + var Math_sqrt = Math.sqrt; + return { + + }; +} + +Running suite: Module +. Running test: create and dispose +. Running test: add custom section +. Running test: write +. Running test: write_text (module - (type $0 (func (param i32 i32) (result i32))) - (type $1 (func)) - (type $2 (func (param anyref i32 i32) (result i32))) - (type $3 (func (param anyref) (result i32))) - (import "future-wasi" "write" (func $write (type $2) (param anyref i32 i32) (result i32))) - (global $max_int64 i64 (i64.const 9223372036854775807)) - (global $max_int64_mut (mut i64) (i64.const 9223372036854775807)) - (global $test_float64_bits f64 (f64.const 1.23)) - (memory $0 1) - (data $hello (i32.const 0) "hello") - (data $world "world") - (table $table 1 1 funcref) - (elem $elem (i32.const 0) $adder) - (export "adder" (func $adder)) - (export "memory" (memory $0)) - (export "hello" (func $hello)) - (start $start) - (func $adder (type $0) (param $0 i32) (param $1 i32) (result i32) - (block $add (result i32) - (if - (i32.const 0) - (then - (unreachable) - ) - ) - (i32.add - (select - (local.get $0) - (i32.load - (local.get $1) - ) - (i32.const 1) - ) - (local.get $1) - ) - ) - ) - (func $start (type $1) - (block $start - (memory.init $world - (i32.const 2048) - (i32.const 0) - (i32.const 5) - ) + (type $0 (func)) + (func $test_func + (block $test_body (drop - (call_indirect $table (type $0) - (i32.const 3) - (i32.const 5) - (i32.const 0) - ) + (i32.const 0) ) ) ) - (func $hello (type $3) (param $0 anyref) (result i32) - (call $write - (local.get $0) - (i32.const 0) - (i32.const 1) - ) - ) ) +. Running test: write_stack_ir (module - (type $0 (func (param i32 i32) (result i32))) - (type $1 (func)) - (type $2 (func (param anyref i32 i32) (result i32))) - (type $3 (func (param anyref) (result i32))) - (import "future-wasi" "write" (func $write (type $2) (param anyref i32 i32) (result i32))) - (memory $0 1) - (data $hello (i32.const 0) "hello") - (data $world "world") - (table $table 1 1 funcref) - (elem $elem (i32.const 0) $adder) - (export "adder" (func $adder)) - (export "memory" (memory $0)) - (export "hello" (func $hello)) - (start $start) - (func $adder (type $0) (param $0 i32) (param $1 i32) (result i32) - (i32.add - (select - (local.get $0) - (i32.load - (local.get $1) - ) - (i32.const 1) - ) - (local.get $1) - ) - ) - (func $start (type $1) - (memory.init $world - (i32.const 2048) - (i32.const 0) - (i32.const 5) - ) - (drop - (call $adder - (i32.const 3) - (i32.const 5) - ) - ) - ) - (func $hello (type $3) (param $0 anyref) (result i32) - (call $write - (local.get $0) - (i32.const 0) - (i32.const 1) - ) + (type $0 (func)) + (func $test_func + i32.const 0 + drop ) ) +. Running test: parse (module - (type $type (func (param anyref i32 i32) (result i32))) - (type $type_1 (func (param i32 i32) (result i32))) - (type $type_2 (func)) - (type $type_3 (func (param anyref) (result i32))) - (import "future-wasi" "write" (func $fimport$0 (type $type) (param anyref i32 i32) (result i32))) - (memory $0 1) - (data $0 (i32.const 0) "hello") - (data $1 "world") - (table $0 1 1 funcref) - (elem $0 (i32.const 0) $0) - (export "adder" (func $0)) - (export "memory" (memory $0)) - (export "hello" (func $2)) - (start $1) - (func $0 (type $type_1) (param $0 i32) (param $1 i32) (result i32) - (i32.add - (select - (local.get $0) - (i32.load - (local.get $1) - ) - (i32.const 1) - ) - (local.get $1) - ) + (type $0 (func)) + (func $0 ) - (func $1 (type $type_2) - (memory.init $1 - (i32.const 2048) - (i32.const 0) - (i32.const 5) - ) +) +. Running test: print +. Running test: print_asmjs +. Running test: print_stack_ir +. Running test: validate +. Running test: optimize +. Running test: get and set features +. Running test: run_empty_passes +. Running test: read +(module + (type $0 (func)) + (func $0 (drop - (call $0 - (i32.const 3) - (i32.const 5) - ) - ) - ) - (func $2 (type $type_3) (param $0 anyref) (result i32) - (call $fimport$0 - (local.get $0) (i32.const 0) - (i32.const 1) ) ) ) +. Running test: interpret +. Running test: debug info filename management +. Running test: update maps +var retasmFunc = asmFunc({ +}); (module - (type $type (func (param anyref i32 i32) (result i32))) - (type $type_1 (func (param i32 i32) (result i32))) - (type $type_2 (func)) - (type $type_3 (func (param anyref) (result i32))) - (import "future-wasi" "write" (func $fimport$0 (type $type) (param anyref i32 i32) (result i32))) - (memory $0 1) - (data $0 (i32.const 0) "hello") - (data $1 "world") - (table $0 1 1 funcref) - (elem $0 (i32.const 0) $0) - (export "adder" (func $0)) - (export "memory" (memory $0)) - (export "hello" (func $2)) - (start $1) - (func $0 (type $type_1) (param $0 i32) (param $1 i32) (result i32) - local.get $0 - local.get $1 - i32.load - i32.const 1 - select - local.get $1 - i32.add - ) - (func $1 (type $type_2) - i32.const 2048 + (type $0 (func)) + (func $test_func i32.const 0 - i32.const 5 - memory.init $1 - i32.const 3 - i32.const 5 - call $0 drop ) - (func $2 (type $type_3) (param $0 anyref) (result i32) - local.get $0 - i32.const 0 - i32.const 1 - call $fimport$0 - ) ) diff --git a/test/test.ml b/test/test.ml index a5ef4e90..df1167d6 100644 --- a/test/test.ml +++ b/test/test.ml @@ -1,4 +1,5 @@ -open Binaryen +open Module +(* open Binaryen (* Testing colors enable *) let _ = assert (Settings.are_colors_enabled () == true) @@ -355,4 +356,4 @@ let _ = Module.print_stack_ir new_mod (* Dispose the modules 👋 *) let _ = Module.dispose wasm_mod -let _ = Module.dispose new_mod +let _ = Module.dispose new_mod *) From b567590820782d8f500339703381de7efe16418a Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Sat, 6 Dec 2025 17:49:08 -0500 Subject: [PATCH 02/11] feat: Implement tests for `Function` & `Settings` --- test/functionTest.re | 179 +++++++++++++++ test/{module.re => moduleTest.re} | 12 +- test/settingsTest.re | 116 ++++++++++ test/test.expected | 65 ++++-- test/test.ml | 359 ------------------------------ test/test.re | 242 ++++++++++++++++++++ 6 files changed, 590 insertions(+), 383 deletions(-) create mode 100644 test/functionTest.re rename test/{module.re => moduleTest.re} (94%) create mode 100644 test/settingsTest.re delete mode 100644 test/test.ml create mode 100644 test/test.re diff --git a/test/functionTest.re b/test/functionTest.re new file mode 100644 index 00000000..f0a33428 --- /dev/null +++ b/test/functionTest.re @@ -0,0 +1,179 @@ +open Binaryen; +open Framework; + +suite("Function", () => { + let create_test_func = (wasm_mod, name) => + Function.add_function( + wasm_mod, + name, + Type.none, + Type.none, + [||], + Expression.Nop.make(wasm_mod), + ); + test("Add Function", () => { + let wasm_mod = Module.create(); + let _ = create_test_func(wasm_mod, "test_func"); + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("get name", () => { + let wasm_mod = Module.create(); + let func_name = "my_function"; + let func = create_test_func(wasm_mod, func_name); + assert(Function.get_name(func) == func_name); + Module.dispose(wasm_mod); + }); + test("get params and results", () => { + let wasm_mod = Module.create(); + let func = + Function.add_function( + wasm_mod, + "param_result_func", + Type.create([|Type.int32, Type.int32|]), + Type.int32, + [||], + Expression.Block.make( + wasm_mod, + ~return_type=Type.int32, + "body", + [Expression.Const.make(wasm_mod, Literal.int32(0l))], + ), + ); + let params = Type.expand(Function.get_params(func)); + assert(Array.length(params) == 2); + let results = Type.expand(Function.get_results(func)); + assert(Array.length(results) == 1); + Module.dispose(wasm_mod); + }); + test("get num vars", () => { + let wasm_mod = Module.create(); + let func = + Function.add_function( + wasm_mod, + "var_func", + Type.none, + Type.none, + [|Type.int32, Type.int64|], + Expression.Block.make( + wasm_mod, + ~return_type=Type.none, + "body", + [ + Expression.Drop.make( + wasm_mod, + Expression.Const.make(wasm_mod, Literal.int32(0l)), + ), + ], + ), + ); + let num_vars = Function.get_num_vars(func); + assert(num_vars == 2); + Module.dispose(wasm_mod); + }); + test("get var", () => { + let wasm_mod = Module.create(); + let func = + Function.add_function( + wasm_mod, + "var_func", + Type.none, + Type.none, + [|Type.create([|Type.int32, Type.int32|])|], + Expression.Block.make( + wasm_mod, + ~return_type=Type.none, + "body", + [ + Expression.Drop.make( + wasm_mod, + Expression.Const.make(wasm_mod, Literal.int32(0l)), + ), + ], + ), + ); + let var0_type = Type.expand(Function.get_var(func, 0)); + assert(Array.length(var0_type) == 2); + Module.dispose(wasm_mod); + }); + test("get/set start", () => { + let wasm_mod = Module.create(); + let func_name = "start_func"; + let func = create_test_func(wasm_mod, func_name); + Function.set_start(wasm_mod, func); + let retrieved_start = Function.get_start(wasm_mod); + assert(Function.get_name(retrieved_start) == func_name); + Module.dispose(wasm_mod); + }); + test("function count", () => { + let wasm_mod = Module.create(); + let _ = create_test_func(wasm_mod, "func1"); + let _ = create_test_func(wasm_mod, "func2"); + let count = Function.get_num_functions(wasm_mod); + assert(count == 2); + Module.dispose(wasm_mod); + }); + test("get func & get func by index", () => { + let wasm_mod = Module.create(); + let func_name = "retrieve_func"; + let _ = create_test_func(wasm_mod, func_name); + let _ = create_test_func(wasm_mod, "another_func"); + let retrieved_func = Function.get_function(wasm_mod, func_name); + + assert(Function.get_name(retrieved_func) == func_name); + + let retrieved_by_index = Function.get_function_by_index(wasm_mod, 0); + assert(Function.get_name(retrieved_by_index) == func_name); + + Module.dispose(wasm_mod); + }); + test("remove_function", () => { + let wasm_mod = Module.create(); + let func_name = "removable_func"; + let _ = create_test_func(wasm_mod, func_name); + Function.remove_function(wasm_mod, func_name); + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("get/set body", () => { + let wasm_mod = Module.create(); + let func = create_test_func(wasm_mod, "body_func"); + let func2 = + Function.add_function( + wasm_mod, + "new_body_func", + Type.none, + Type.none, + [||], + Expression.Drop.make( + wasm_mod, + Expression.Const.make(wasm_mod, Literal.int32(42l)), + ), + ); + let body = Function.get_body(func); + Function.set_body(func2, body); // Swap bodies for test side effect + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("set_debug_location", () => { + let wasm_mod = Module.create(); + let exp = Expression.Nop.make(wasm_mod); + let func = + Function.add_function( + wasm_mod, + "debug_loc_func", + Type.none, + Type.none, + [||], + exp, + ); + Function.set_debug_location(func, exp, 0, 10, 20); + let source_map = + switch (Module.write(wasm_mod, Some("file"))) { + | (_, Some(sm)) => sm + | _ => assert(false) + }; + Printf.printf("%s", source_map); + Module.dispose(wasm_mod); + }); +}); diff --git a/test/module.re b/test/moduleTest.re similarity index 94% rename from test/module.re rename to test/moduleTest.re index d6f313d5..121407d4 100644 --- a/test/module.re +++ b/test/moduleTest.re @@ -10,17 +10,7 @@ suite("Module", () => { Type.none, Type.none, [||], - Expression.Block.make( - wasm_mod, - ~return_type=Type.none, - "test_body", - [ - Expression.Drop.make( - wasm_mod, - Expression.Const.make(wasm_mod, Literal.int32(0l)), - ), - ], - ), + Expression.Nop.make(wasm_mod), ); (); }; diff --git a/test/settingsTest.re b/test/settingsTest.re new file mode 100644 index 00000000..039cba15 --- /dev/null +++ b/test/settingsTest.re @@ -0,0 +1,116 @@ +open Binaryen; +open Framework; + +suite("Settings", () => { + test("optimize_level", () => { + let default_level = Settings.get_optimize_level(); + assert(Settings.get_optimize_level() != 3); + Settings.set_optimize_level(3); + assert(Settings.get_optimize_level() == 3); + Settings.set_optimize_level(default_level); + }); + test("shrink_level", () => { + let default_level = Settings.get_shrink_level(); + assert(Settings.get_shrink_level() != 2); + Settings.set_shrink_level(2); + assert(Settings.get_shrink_level() == 2); + Settings.set_shrink_level(default_level); + }); + test("debug_info", () => { + assert(Settings.get_debug_info() == false); + Settings.set_debug_info(true); + assert(Settings.get_debug_info() == true); + Settings.set_debug_info(false); + }); + test("traps_never_happen", () => { + assert(Settings.get_traps_never_happen() == false); + Settings.set_traps_never_happen(true); + assert(Settings.get_traps_never_happen() == true); + Settings.set_traps_never_happen(false); + }); + test("closed_world", () => { + assert(Settings.get_closed_world() == false); + Settings.set_closed_world(true); + assert(Settings.get_closed_world() == true); + Settings.set_closed_world(false); + }); + test("low_memory_unused", () => { + assert(Settings.get_low_memory_unused() == false); + Settings.set_low_memory_unused(true); + assert(Settings.get_low_memory_unused() == true); + Settings.set_low_memory_unused(false); + }); + test("zero_filled_memory", () => { + assert(Settings.get_zero_filled_memory() == false); + Settings.set_zero_filled_memory(true); + assert(Settings.get_zero_filled_memory() == true); + Settings.set_zero_filled_memory(false); + }); + test("fast_math", () => { + assert(Settings.get_fast_math() == false); + Settings.set_fast_math(true); + assert(Settings.get_fast_math() == true); + Settings.set_fast_math(false); + }); + test("generate_stack_ir", () => { + assert(Settings.get_generate_stack_ir() == false); + Settings.set_generate_stack_ir(true); + assert(Settings.get_generate_stack_ir() == true); + Settings.set_generate_stack_ir(false); + }); + test("optimize_stack_ir", () => { + assert(Settings.get_optimize_stack_ir() == false); + Settings.set_optimize_stack_ir(true); + assert(Settings.get_optimize_stack_ir() == true); + Settings.set_optimize_stack_ir(false); + }); + test("pass_argument", () => { + assert(Settings.get_pass_argument("theKey") == None); + Settings.set_pass_argument("theKey", "theValue"); + assert(Settings.get_pass_argument("theKey") == Some("theValue")); + Settings.set_pass_argument("theKey", "theValue2"); + assert(Settings.get_pass_argument("theKey") == Some("theValue2")); + Settings.clear_pass_arguments(); + assert(Settings.get_pass_argument("theKey") == None); + }); + test("skip_pass", () => { + assert(Settings.has_pass_to_skip("thePass") == false); + Settings.add_pass_to_skip("thePass"); + assert(Settings.has_pass_to_skip("thePass") == true); + Settings.clear_passes_to_skip(); + assert(Settings.has_pass_to_skip("thePass") == false); + }); + test("always inline max size", () => { + let default_size = Settings.get_always_inline_max_size(); + assert(Settings.get_always_inline_max_size() != 1); + Settings.set_always_inline_max_size(1); + assert(Settings.get_always_inline_max_size() == 1); + Settings.set_always_inline_max_size(default_size); + }); + test("flexible inline max size", () => { + let default_size = Settings.get_flexible_inline_max_size(); + assert(Settings.get_flexible_inline_max_size() != 1); + Settings.set_flexible_inline_max_size(1); + assert(Settings.get_flexible_inline_max_size() == 1); + Settings.set_flexible_inline_max_size(default_size); + }); + test("one caller inline max size", () => { + let default_size = Settings.get_one_caller_inline_max_size(); + assert(Settings.get_one_caller_inline_max_size() != 1); + Settings.set_one_caller_inline_max_size(1); + assert(Settings.get_one_caller_inline_max_size() == 1); + Settings.set_one_caller_inline_max_size(default_size); + }); + test("allow inlining functions with loops", () => { + assert(Settings.get_allow_inlining_functions_with_loops() == false); + Settings.set_allow_inlining_functions_with_loops(true); + assert(Settings.get_allow_inlining_functions_with_loops() == true); + Settings.set_allow_inlining_functions_with_loops(false); + }); + test("colors enabled", () => { + assert(Settings.are_colors_enabled() == true); + Settings.set_colors_enabled(false); + assert(Settings.are_colors_enabled() == false); + Settings.set_colors_enabled(true); + }); +}); diff --git a/test/test.expected b/test/test.expected index 2339df93..53a962b7 100644 --- a/test/test.expected +++ b/test/test.expected @@ -17,7 +17,36 @@ function asmFunc(imports) { }; } -Running suite: Module +Running suite: Function +. Running test: Add Function +(module + (type $0 (func)) + (func $test_func + (nop) + ) +) +. Running test: get name +. Running test: get params and results +. Running test: get num vars +. Running test: get var +. Running test: get/set start +. Running test: function count +. Running test: get func & get func by index +. Running test: remove_function +(module +) +. Running test: get/set body +(module + (type $0 (func)) + (func $body_func + (nop) + ) + (func $new_body_func + (nop) + ) +) +. Running test: set_debug_location +{"version":3,"sources":[],"names":[],"mappings":"uBASoB,C"}Running suite: Module . Running test: create and dispose . Running test: add custom section . Running test: write @@ -25,19 +54,14 @@ Running suite: Module (module (type $0 (func)) (func $test_func - (block $test_body - (drop - (i32.const 0) - ) - ) + (nop) ) ) . Running test: write_stack_ir (module (type $0 (func)) (func $test_func - i32.const 0 - drop + nop ) ) . Running test: parse @@ -57,20 +81,35 @@ Running suite: Module (module (type $0 (func)) (func $0 - (drop - (i32.const 0) - ) + (nop) ) ) . Running test: interpret . Running test: debug info filename management . Running test: update maps +Running suite: Settings +. Running test: optimize_level +. Running test: shrink_level +. Running test: debug_info +. Running test: traps_never_happen +. Running test: closed_world +. Running test: low_memory_unused +. Running test: zero_filled_memory +. Running test: fast_math +. Running test: generate_stack_ir +. Running test: optimize_stack_ir +. Running test: pass_argument +. Running test: skip_pass +. Running test: always inline max size +. Running test: flexible inline max size +. Running test: one caller inline max size +. Running test: allow inlining functions with loops +. Running test: colors enabled var retasmFunc = asmFunc({ }); (module (type $0 (func)) (func $test_func - i32.const 0 - drop + nop ) ) diff --git a/test/test.ml b/test/test.ml deleted file mode 100644 index df1167d6..00000000 --- a/test/test.ml +++ /dev/null @@ -1,359 +0,0 @@ -open Module -(* open Binaryen - -(* Testing colors enable *) -let _ = assert (Settings.are_colors_enabled () == true) -let _ = Settings.set_colors_enabled false -let _ = assert (Settings.are_colors_enabled () == false) -let _ = Settings.set_colors_enabled true - -(* Testing debug_info enable *) -let _ = assert (Settings.get_debug_info () == false) -let _ = Settings.set_debug_info true -let _ = assert (Settings.get_debug_info () == true) -let _ = Settings.set_debug_info false - -(* Testing traps_never_happen enable *) -let _ = assert (Settings.get_traps_never_happen () == false) -let _ = Settings.set_traps_never_happen true -let _ = assert (Settings.get_traps_never_happen () == true) -let _ = Settings.set_traps_never_happen false - -(* Testing closed_world enable *) -let _ = assert (Settings.get_closed_world () == false) -let _ = Settings.set_closed_world true -let _ = assert (Settings.get_closed_world () == true) -let _ = Settings.set_closed_world false - -(* Testing low_memory_unused enable *) -let _ = assert (Settings.get_low_memory_unused () == false) -let _ = Settings.set_low_memory_unused true -let _ = assert (Settings.get_low_memory_unused () == true) -let _ = Settings.set_low_memory_unused false - -(* Testing zero_filled_memory enable *) -let _ = assert (Settings.get_zero_filled_memory () == false) -let _ = Settings.set_zero_filled_memory true -let _ = assert (Settings.get_zero_filled_memory () == true) -let _ = Settings.set_zero_filled_memory false - -(* Testing fast_math enable *) -let _ = assert (Settings.get_fast_math () == false) -let _ = Settings.set_fast_math true -let _ = assert (Settings.get_fast_math () == true) -let _ = Settings.set_fast_math false - -(* Testing generate_stack_ir enable *) -let _ = assert (Settings.get_generate_stack_ir () == false) -let _ = Settings.set_generate_stack_ir true -let _ = assert (Settings.get_generate_stack_ir () == true) -let _ = Settings.set_generate_stack_ir false - -(* Testing optimize_stack_ir enable *) -let _ = assert (Settings.get_optimize_stack_ir () == false) -let _ = Settings.set_optimize_stack_ir true -let _ = assert (Settings.get_optimize_stack_ir () == true) -let _ = Settings.set_optimize_stack_ir false - -(* Testing pass_argument *) -let _ = assert (Settings.get_pass_argument "theKey" = None) -let _ = Settings.set_pass_argument "theKey" "theValue" -let _ = assert (Settings.get_pass_argument "theKey" = Some "theValue") -let _ = Settings.clear_pass_arguments () -let _ = assert (Settings.get_pass_argument "theKey" = None) - -(* Testing skip_pass *) -let _ = assert (Settings.has_pass_to_skip "thePass" == false) -let _ = Settings.add_pass_to_skip "thePass" -let _ = assert (Settings.has_pass_to_skip "thePass" == true) -let _ = Settings.clear_passes_to_skip () -let _ = assert (Settings.has_pass_to_skip "thePass" == false) - -(* Testing allow_inlining_functions_with_loops enable *) -let _ = assert (Settings.get_allow_inlining_functions_with_loops () == false) -let _ = Settings.set_allow_inlining_functions_with_loops true -let _ = assert (Settings.get_allow_inlining_functions_with_loops () == true) -let _ = Settings.set_allow_inlining_functions_with_loops false -let wasm_mod = Module.create () -let _ = Module.set_features wasm_mod [ Module.Feature.all ] -let import_wasm_mod = Module.create () - -(* Testing pass_argument *) -let _ = assert (Settings.get_pass_argument "theKey" = None) -let _ = Settings.set_pass_argument "theKey" "theValue" -let _ = assert (Settings.get_pass_argument "theKey" = Some "theValue") -let _ = Settings.set_pass_argument "theKey" "theValue2" -let _ = assert (Settings.get_pass_argument "theKey" = Some "theValue2") - -let _ = - Import.add_memory_import import_wasm_mod "internal_name" "external_name" - "external_base_name" true - -let _ = - assert ( - Import.memory_import_get_module import_wasm_mod "internal_name" - = "external_name") - -let _ = - assert ( - Import.memory_import_get_base import_wasm_mod "internal_name" - = "external_base_name") - -let _ = assert (Memory.is_shared import_wasm_mod "internal_name" = true) -let _ = assert (Memory.is_64 import_wasm_mod "internal_name" = false) - -(* Testing Return.get_value *) -let _ = - assert ( - Option.is_some - (Expression.Return.get_value - (Expression.Return.make wasm_mod - (Expression.Const.make wasm_mod (Literal.int32 0l))))) - -let _ = - assert ( - Option.is_none - (Expression.Return.get_value - (Expression.Return.make wasm_mod (Expression.Null.make ())))) - -(* Create function type for i32 (i32, i32) *) -let params () = Type.create [| Type.int32; Type.int32 |] -let results = Type.int32 -let x () = Expression.Local_get.make wasm_mod 0 Type.int32 -let y () = Expression.Local_get.make wasm_mod 1 Type.int32 - -let load = - Expression.Load.make wasm_mod 4 ~signed:false 0 0 Type.int32 (y ()) "0" - -let select = - Expression.Select.make wasm_mod - (Expression.Const.make wasm_mod (Literal.int32 1l)) - (x ()) load - -let bin = Expression.Binary.make wasm_mod Op.add_int32 select (y ()) - -let if_ = - Expression.If.make wasm_mod - (Expression.Const.make wasm_mod (Literal.int32 0l)) - (Expression.Unreachable.make wasm_mod) - (Expression.Null.make ()) - -let _ = assert (Expression.If.get_if_false if_ = None) - -let add = - Expression.Block.make wasm_mod ~return_type:Type.int32 "add" [ if_; bin ] - -let _ = assert (Expression.Block.get_name add = Some "add") - -(* Create the add function *) -let _adder = Function.add_function wasm_mod "adder" (params ()) results [||] add - -let call_adder = - Expression.Call_indirect.make wasm_mod "table" - (Expression.Const.make wasm_mod (Literal.int32 0l)) - [ - Expression.Const.make wasm_mod (Literal.int32 3l); - Expression.Const.make wasm_mod (Literal.int32 5l); - ] - (params ()) Type.int32 - -let start = - Function.add_function wasm_mod "start" Type.none Type.none [||] - (Expression.Block.make wasm_mod ~return_type:Type.none "start" - [ - Expression.Memory_init.make wasm_mod "world" - (Expression.Const.make wasm_mod (Literal.int32 2048l)) - (Expression.Const.make wasm_mod (Literal.int32 0l)) - (Expression.Const.make wasm_mod (Literal.int32 5l)) - "0"; - Expression.Drop.make wasm_mod call_adder; - ]) - -let _ = Export.add_function_export wasm_mod "adder" "adder" -let _ = Table.add_table wasm_mod "table" 1 1 Type.funcref - -(* TODO(#240): Re-enable after type-builder api is merged *) -(* let funcref_expr1 = Expression.Ref.func wasm_mod "adder" (Heap_type.func ()) - -let _ = - Expression.Table.set wasm_mod "table" - (Expression.Const.make wasm_mod (Literal.int32 0l)) - funcref_expr1 *) - -let funcref_expr2 = - Expression.Table.get wasm_mod "table" - (Expression.Const.make wasm_mod (Literal.int32 0l)) - Type.funcref - -let _ = Expression.print funcref_expr2 -let table_size = Expression.Table.size wasm_mod "table" -let _ = Expression.print table_size -let table_name = Expression.Table_size.get_table table_size -let _ = Expression.Table_size.set_table table_size table_name -let null_ref = Expression.Ref.null wasm_mod Type.funcref - -let table_grow = - Expression.Table.grow wasm_mod "table" null_ref - (Expression.Const.make wasm_mod (Literal.int32 0l)) - -let _ = Expression.print table_grow - -let _ = - Global.add_global wasm_mod "max_int64" Type.int64 false - (Expression.Const.make wasm_mod (Literal.int64 Int64.max_int)) - -let _ = - Global.add_global wasm_mod "max_int64_mut" Type.int64 true - (Expression.Const.make wasm_mod (Literal.int64 Int64.max_int)) - -let _ = - Global.add_global wasm_mod "test_float64_bits" Type.float64 false - (Expression.Const.make wasm_mod (Literal.float64_bits 0x3FF3AE147AE147AEL)) - -let _ = - Table.add_active_element_segment wasm_mod "table" "elem" [ "adder" ] - (Expression.Const.make wasm_mod (Literal.int32 0l)) - -let _ = Function.set_start wasm_mod start -let start_func = Function.get_start wasm_mod -let _ = assert (Function.get_name start_func = "start") - -let segment : Binaryen.Memory.segment = - let data = Bytes.of_string "hello" in - let kind = - Binaryen.Memory.Active - { offset = Expression.Const.make wasm_mod (Literal.int32 0l) } - in - let size = Bytes.length data in - { name = "hello"; data; kind; size } - -let passive_segment : Binaryen.Memory.segment = - let data = Bytes.of_string "world" in - let kind = Binaryen.Memory.Passive in - let size = Bytes.length data in - { name = "world"; data; kind; size } - -let _ = assert (Memory.has_memory wasm_mod = false) - -let _ = - Memory.set_memory wasm_mod 1 Memory.unlimited "memory" - [ segment; passive_segment ] - false false "0" - -let _ = assert (Memory.has_memory wasm_mod = true) -let _ = assert (Memory.get_initial wasm_mod "0" = 1) -let _ = assert (Memory.has_max wasm_mod "0" = false) -let _ = assert (Memory.get_max wasm_mod "0" = Memory.unlimited) -let max_memory_wasm_mod = Module.create () -let _ = Memory.set_memory max_memory_wasm_mod 1 2 "memory" [] false false "0" -let _ = assert (Memory.has_max max_memory_wasm_mod "0" = true) -let _ = assert (Memory.get_max max_memory_wasm_mod "0" = 2) - -let _ = - assert ( - Bytes.equal - (Memory.get_segment_data wasm_mod "world") - (Bytes.of_string "world")) - -(* Create an imported "write" function i32 (externref, i32, i32) *) -(* Similar to the example here: https://bytecodealliance.org/articles/reference-types-in-wasmtime *) - -let _ = - Import.add_function_import wasm_mod "write" "future-wasi" "write" - (Type.create [| Type.anyref; Type.int32; Type.int32 |]) - Type.int32 - -(* Create a function that calls the imported write function *) -let _ = - Function.add_function wasm_mod "hello" Type.anyref Type.int32 [||] - (Expression.Call.make wasm_mod "write" - [ - Expression.Local_get.make wasm_mod 0 Type.anyref; - Expression.Const.make wasm_mod (Literal.int32 0l); - Expression.Const.make wasm_mod (Literal.int32 1l); - ] - Type.int32) - -let _ = Export.add_function_export wasm_mod "hello" "hello" -let _ = Module.validate wasm_mod - -(* Shouldn't actually do anything since we aren't doing function renames *) -let _ = Module.update_maps wasm_mod - -(* Finally, we print 3 versions of the module to be checked against test.expected *) - -(* 1. Print the the module as-is *) - -let _ = Module.print wasm_mod - -(* 2. Optimize, then print the module *) - -let _ = Module.optimize wasm_mod -let _ = Module.print wasm_mod - -(* 3. Copy previous module bytes into new module, validate, and print *) - -let byts, no_sourcemap = Module.write wasm_mod None -let _ = assert (no_sourcemap = None) -let _, sourcemap = Module.write wasm_mod (Some "test.wasm.map") - -let _ = - assert ( - sourcemap = Some {|{"version":3,"sources":[],"names":[],"mappings":""}|}) - -let new_mod = Module.read byts - -let _ = - Module.run_passes new_mod - [ - Passes.generate_global_effects; - Passes.name_types; - Passes.merge_similar_functions; - Passes.spill_pointers; - Passes.gufa; - Passes.gufa_optimizing; - Passes.reorder_globals; - Passes.optimize_casts; - Passes.multi_memory_lowering; - Passes.monomorphize; - Passes.signext_lowering; - Passes.discard_global_effects; - Passes.strip_eh; - ] - -let _ = - Module.set_features new_mod - [ - Module.Feature.mvp; - Module.Feature.atomics; - Module.Feature.mutable_globals; - Module.Feature.nontrapping_fp_to_int; - Module.Feature.simd128; - Module.Feature.bulk_memory; - Module.Feature.sign_ext; - Module.Feature.exception_handling; - Module.Feature.tail_call; - Module.Feature.reference_types; - Module.Feature.multivalue; - Module.Feature.gc; - Module.Feature.memory64; - Module.Feature.relaxed_simd; - Module.Feature.extended_const; - Module.Feature.strings; - Module.Feature.multi_memory; - Module.Feature.stack_switching; - Module.Feature.shared_everything; - Module.Feature.fp16; - Module.Feature.bulk_memory_opt; - Module.Feature.call_indirect_overlong; - Module.Feature.all; - ] - -let _ = Module.validate new_mod -let _ = Module.print new_mod -let _ = Module.print_stack_ir new_mod - -(* Dispose the modules 👋 *) - -let _ = Module.dispose wasm_mod -let _ = Module.dispose new_mod *) diff --git a/test/test.re b/test/test.re new file mode 100644 index 00000000..f229316b --- /dev/null +++ b/test/test.re @@ -0,0 +1,242 @@ +open ModuleTest; +open FunctionTest; +open SettingsTest; +// TODO: Type +// TODO: Table +// TODO: Struct_Type +// TODO: Signature_Type +// TODO: Passes +// TODO: Packed_Type +// TODO: OP +// TODO: Memory +// TODO: Literal +// TODO: Import +// TODO: Heap_Type +// TODO: Global +// TODO: Expression +// TODO: Export +// TODO: Element_Segment +// TODO: Array_Type + +// let wasm_mod = Module.create () +// let _ = Module.set_features wasm_mod [ Module.Feature.all ] +// let import_wasm_mod = Module.create () + +// (* Testing pass_argument *) +// let _ = assert (Settings.get_pass_argument "theKey" = None) +// let _ = Settings.set_pass_argument "theKey" "theValue" +// let _ = assert (Settings.get_pass_argument "theKey" = Some "theValue") +// let _ = Settings.set_pass_argument "theKey" "theValue2" +// let _ = assert (Settings.get_pass_argument "theKey" = Some "theValue2") + +// let _ = +// Import.add_memory_import import_wasm_mod "internal_name" "external_name" +// "external_base_name" true + +// let _ = +// assert ( +// Import.memory_import_get_module import_wasm_mod "internal_name" +// = "external_name") + +// let _ = +// assert ( +// Import.memory_import_get_base import_wasm_mod "internal_name" +// = "external_base_name") + +// let _ = assert (Memory.is_shared import_wasm_mod "internal_name" = true) +// let _ = assert (Memory.is_64 import_wasm_mod "internal_name" = false) + +// (* Testing Return.get_value *) +// let _ = +// assert ( +// Option.is_some +// (Expression.Return.get_value +// (Expression.Return.make wasm_mod +// (Expression.Const.make wasm_mod (Literal.int32 0l))))) + +// let _ = +// assert ( +// Option.is_none +// (Expression.Return.get_value +// (Expression.Return.make wasm_mod (Expression.Null.make ())))) + +// (* Create function type for i32 (i32, i32) *) +// let params () = Type.create [| Type.int32; Type.int32 |] +// let results = Type.int32 +// let x () = Expression.Local_get.make wasm_mod 0 Type.int32 +// let y () = Expression.Local_get.make wasm_mod 1 Type.int32 + +// let load = +// Expression.Load.make wasm_mod 4 ~signed:false 0 0 Type.int32 (y ()) "0" + +// let select = +// Expression.Select.make wasm_mod +// (Expression.Const.make wasm_mod (Literal.int32 1l)) +// (x ()) load + +// let bin = Expression.Binary.make wasm_mod Op.add_int32 select (y ()) + +// let if_ = +// Expression.If.make wasm_mod +// (Expression.Const.make wasm_mod (Literal.int32 0l)) +// (Expression.Unreachable.make wasm_mod) +// (Expression.Null.make ()) + +// let _ = assert (Expression.If.get_if_false if_ = None) + +// let add = +// Expression.Block.make wasm_mod ~return_type:Type.int32 "add" [ if_; bin ] + +// let _ = assert (Expression.Block.get_name add = Some "add") + +// (* Create the add function *) +// let _adder = Function.add_function wasm_mod "adder" (params ()) results [||] add + +// let call_adder = +// Expression.Call_indirect.make wasm_mod "table" +// (Expression.Const.make wasm_mod (Literal.int32 0l)) +// [ +// Expression.Const.make wasm_mod (Literal.int32 3l); +// Expression.Const.make wasm_mod (Literal.int32 5l); +// ] +// (params ()) Type.int32 + +// let start = +// Function.add_function wasm_mod "start" Type.none Type.none [||] +// (Expression.Block.make wasm_mod ~return_type:Type.none "start" +// [ +// Expression.Memory_init.make wasm_mod "world" +// (Expression.Const.make wasm_mod (Literal.int32 2048l)) +// (Expression.Const.make wasm_mod (Literal.int32 0l)) +// (Expression.Const.make wasm_mod (Literal.int32 5l)) +// "0"; +// Expression.Drop.make wasm_mod call_adder; +// ]) + +// let _ = Export.add_function_export wasm_mod "adder" "adder" +// let _ = Table.add_table wasm_mod "table" 1 1 Type.funcref + +// (* TODO(#240): Re-enable after type-builder api is merged *) +// (* let funcref_expr1 = Expression.Ref.func wasm_mod "adder" (Heap_type.func ()) + +// let _ = +// Expression.Table.set wasm_mod "table" +// (Expression.Const.make wasm_mod (Literal.int32 0l)) +// funcref_expr1 *) + +// let funcref_expr2 = +// Expression.Table.get wasm_mod "table" +// (Expression.Const.make wasm_mod (Literal.int32 0l)) +// Type.funcref + +// let _ = Expression.print funcref_expr2 +// let table_size = Expression.Table.size wasm_mod "table" +// let _ = Expression.print table_size +// let table_name = Expression.Table_size.get_table table_size +// let _ = Expression.Table_size.set_table table_size table_name +// let null_ref = Expression.Ref.null wasm_mod Type.funcref + +// let table_grow = +// Expression.Table.grow wasm_mod "table" null_ref +// (Expression.Const.make wasm_mod (Literal.int32 0l)) + +// let _ = Expression.print table_grow + +// let _ = +// Global.add_global wasm_mod "max_int64" Type.int64 false +// (Expression.Const.make wasm_mod (Literal.int64 Int64.max_int)) + +// let _ = +// Global.add_global wasm_mod "max_int64_mut" Type.int64 true +// (Expression.Const.make wasm_mod (Literal.int64 Int64.max_int)) + +// let _ = +// Global.add_global wasm_mod "test_float64_bits" Type.float64 false +// (Expression.Const.make wasm_mod (Literal.float64_bits 0x3FF3AE147AE147AEL)) + +// let _ = +// Table.add_active_element_segment wasm_mod "table" "elem" [ "adder" ] +// (Expression.Const.make wasm_mod (Literal.int32 0l)) + +// let _ = Function.set_start wasm_mod start +// let start_func = Function.get_start wasm_mod +// let _ = assert (Function.get_name start_func = "start") + +// let segment : Binaryen.Memory.segment = +// let data = Bytes.of_string "hello" in +// let kind = +// Binaryen.Memory.Active +// { offset = Expression.Const.make wasm_mod (Literal.int32 0l) } +// in +// let size = Bytes.length data in +// { name = "hello"; data; kind; size } + +// let passive_segment : Binaryen.Memory.segment = +// let data = Bytes.of_string "world" in +// let kind = Binaryen.Memory.Passive in +// let size = Bytes.length data in +// { name = "world"; data; kind; size } + +// let _ = assert (Memory.has_memory wasm_mod = false) + +// let _ = +// Memory.set_memory wasm_mod 1 Memory.unlimited "memory" +// [ segment; passive_segment ] +// false false "0" + +// let _ = assert (Memory.has_memory wasm_mod = true) +// let _ = assert (Memory.get_initial wasm_mod "0" = 1) +// let _ = assert (Memory.has_max wasm_mod "0" = false) +// let _ = assert (Memory.get_max wasm_mod "0" = Memory.unlimited) +// let max_memory_wasm_mod = Module.create () +// let _ = Memory.set_memory max_memory_wasm_mod 1 2 "memory" [] false false "0" +// let _ = assert (Memory.has_max max_memory_wasm_mod "0" = true) +// let _ = assert (Memory.get_max max_memory_wasm_mod "0" = 2) + +// let _ = +// assert ( +// Bytes.equal +// (Memory.get_segment_data wasm_mod "world") +// (Bytes.of_string "world")) + +// (* Create an imported "write" function i32 (externref, i32, i32) *) +// (* Similar to the example here: https://bytecodealliance.org/articles/reference-types-in-wasmtime *) + +// let _ = +// Import.add_function_import wasm_mod "write" "future-wasi" "write" +// (Type.create [| Type.anyref; Type.int32; Type.int32 |]) +// Type.int32 + +// (* Create a function that calls the imported write function *) +// let _ = +// Function.add_function wasm_mod "hello" Type.anyref Type.int32 [||] +// (Expression.Call.make wasm_mod "write" +// [ +// Expression.Local_get.make wasm_mod 0 Type.anyref; +// Expression.Const.make wasm_mod (Literal.int32 0l); +// Expression.Const.make wasm_mod (Literal.int32 1l); +// ] +// Type.int32) + +// let _ = Export.add_function_export wasm_mod "hello" "hello" +// let _ = Module.validate wasm_mod + +// let new_mod = Module.read byts + +// let _ = +// Module.run_passes new_mod +// [ +// Passes.generate_global_effects; +// Passes.name_types; +// Passes.merge_similar_functions; +// Passes.spill_pointers; +// Passes.gufa; +// Passes.gufa_optimizing; +// Passes.reorder_globals; +// Passes.optimize_casts; +// Passes.multi_memory_lowering; +// Passes.monomorphize; +// Passes.signext_lowering; +// Passes.discard_global_effects; +// Passes.strip_eh; +// ] From 3c36c792ff83bc525c1e774b8ee07e7e5a23fe1d Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Sat, 6 Dec 2025 18:02:51 -0500 Subject: [PATCH 03/11] feat: Implement tests for `Type` --- test/{framework.re => frame_work.re} | 0 test/{framework.rei => frame_work.rei} | 0 test/{functionTest.re => function_test.re} | 2 +- test/{moduleTest.re => module_test.re} | 2 +- test/{settingsTest.re => settings_test.re} | 2 +- test/test.expected | 31 ++++++++ test/test.re | 7 +- test/type_test.re | 82 ++++++++++++++++++++++ 8 files changed, 120 insertions(+), 6 deletions(-) rename test/{framework.re => frame_work.re} (100%) rename test/{framework.rei => frame_work.rei} (100%) rename test/{functionTest.re => function_test.re} (99%) rename test/{moduleTest.re => module_test.re} (99%) rename test/{settingsTest.re => settings_test.re} (99%) create mode 100644 test/type_test.re diff --git a/test/framework.re b/test/frame_work.re similarity index 100% rename from test/framework.re rename to test/frame_work.re diff --git a/test/framework.rei b/test/frame_work.rei similarity index 100% rename from test/framework.rei rename to test/frame_work.rei diff --git a/test/functionTest.re b/test/function_test.re similarity index 99% rename from test/functionTest.re rename to test/function_test.re index f0a33428..a7d0cd33 100644 --- a/test/functionTest.re +++ b/test/function_test.re @@ -1,5 +1,5 @@ open Binaryen; -open Framework; +open Frame_work; suite("Function", () => { let create_test_func = (wasm_mod, name) => diff --git a/test/moduleTest.re b/test/module_test.re similarity index 99% rename from test/moduleTest.re rename to test/module_test.re index 121407d4..3b39cab5 100644 --- a/test/moduleTest.re +++ b/test/module_test.re @@ -1,5 +1,5 @@ open Binaryen; -open Framework; +open Frame_work; suite("Module", () => { let create_test_func = wasm_mod => { diff --git a/test/settingsTest.re b/test/settings_test.re similarity index 99% rename from test/settingsTest.re rename to test/settings_test.re index 039cba15..112805ed 100644 --- a/test/settingsTest.re +++ b/test/settings_test.re @@ -1,5 +1,5 @@ open Binaryen; -open Framework; +open Frame_work; suite("Settings", () => { test("optimize_level", () => { diff --git a/test/test.expected b/test/test.expected index 53a962b7..0b2a02ed 100644 --- a/test/test.expected +++ b/test/test.expected @@ -105,6 +105,37 @@ Running suite: Settings . Running test: one caller inline max size . Running test: allow inlining functions with loops . Running test: colors enabled +Running suite: Type +. Running test: none +(module + (type $0 (func)) + (func $none_type_test + (nop) + ) +) +. Running test: create/expand +. Running test: basic types +(module + (type $0 (func (param i32 i64 f32 f64 v128))) + (func $basic_type_test (param $0 i32) (param $1 i64) (param $2 f32) (param $3 f64) (param $4 v128) + (nop) + ) +) +. Running test: ref types +(module + (type $0 (func (param funcref anyref eqref i31ref structref arrayref stringref nullref nullexternref nullfuncref))) + (func $ref_type_test (param $0 funcref) (param $1 anyref) (param $2 eqref) (param $3 i31ref) (param $4 structref) (param $5 arrayref) (param $6 stringref) (param $7 nullref) (param $8 nullexternref) (param $9 nullfuncref) + (nop) + ) +) +. Running test: unreachable type +(module + (type $0 (func (param unreachable))) + (func $unreachable_type_test (param $0 unreachable) + (nop) + ) +) +. Running test: is_nullable var retasmFunc = asmFunc({ }); (module diff --git a/test/test.re b/test/test.re index f229316b..57d0ea0c 100644 --- a/test/test.re +++ b/test/test.re @@ -1,6 +1,7 @@ -open ModuleTest; -open FunctionTest; -open SettingsTest; +open Module_test; +open Function_test; +open Settings_test; +open Type_test; // TODO: Type // TODO: Table // TODO: Struct_Type diff --git a/test/type_test.re b/test/type_test.re new file mode 100644 index 00000000..b60e9d27 --- /dev/null +++ b/test/type_test.re @@ -0,0 +1,82 @@ +open Binaryen; +open Frame_work; + +suite("Type", () => { + // NOTE: Type.auto isn't tested as it is not a real type + + let create_test_func = (wasm_mod, name, param_type) => { + Function.add_function( + wasm_mod, + name, + param_type, + Type.none, + [||], + Expression.Nop.make(wasm_mod), + ); + }; + test("none", () => { + let wasm_mod = Module.create(); + let _ = create_test_func(wasm_mod, "none_type_test", Type.none); + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("create/expand", () => { + let expanded = Type.expand(Type.int32); + assert(Array.length(expanded) == 1); + let typ = Type.create([|Type.int32, Type.int32|]); + let expanded = Type.expand(typ); + assert(Array.length(expanded) == 2); + }); + test("basic types", () => { + let wasm_mod = Module.create(); + let _ = + create_test_func( + wasm_mod, + "basic_type_test", + Type.create([| + Type.int32, + Type.int64, + Type.float32, + Type.float64, + Type.vec128, + |]), + ); + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("ref types", () => { + let wasm_mod = Module.create(); + let _ = + create_test_func( + wasm_mod, + "ref_type_test", + Type.create([| + Type.funcref, + Type.anyref, + Type.eqref, + Type.i31ref, + Type.structref, + Type.arrayref, + Type.stringref, + Type.nullref, + Type.null_externref, + Type.null_funcref, + |]), + ); + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("unreachable type", () => { + let wasm_mod = Module.create(); + let _ = + create_test_func(wasm_mod, "unreachable_type_test", Type.unreachable); + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("is_nullable", () => { + assert(Type.is_nullable(Type.anyref) == true); + assert(Type.is_nullable(Type.int32) == false); + }); + // TODO: Test from_heap_type + // TODO: Test get_heap_type +}); From ca28d84faa2467740ac64ee7f3bcae0b9fb9218f Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Sat, 6 Dec 2025 18:10:30 -0500 Subject: [PATCH 04/11] feat: Implement tests for `Literal` --- test/literal_test.re | 61 ++++++++++++++++++++++++++++++++++++++++++++ test/test.expected | 57 ++++++++++++++++++++++++++++++++++++++++- test/test.re | 3 +-- 3 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 test/literal_test.re diff --git a/test/literal_test.re b/test/literal_test.re new file mode 100644 index 00000000..1c0a7955 --- /dev/null +++ b/test/literal_test.re @@ -0,0 +1,61 @@ +open Binaryen; +open Frame_work; + +suite("Literal", () => { + let create_test_func = (wasm_mod, name, lit) => { + Function.add_function( + wasm_mod, + name, + Type.none, + Type.none, + [||], + Expression.Drop.make(wasm_mod, Expression.Const.make(wasm_mod, lit)), + ); + }; + test("int32", () => { + let wasm_mod = Module.create(); + let _ = create_test_func(wasm_mod, "int32_test", Literal.int32(42l)); + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("int64", () => { + let wasm_mod = Module.create(); + let _ = create_test_func(wasm_mod, "int64_test", Literal.int64(-35L)); + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("float32_bits", () => { + let wasm_mod = Module.create(); + let _ = + create_test_func( + wasm_mod, + "float32_bits_test", + Literal.float32_bits(0x40900000l), + ); // 4.5 + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("float64_bits", () => { + let wasm_mod = Module.create(); + let _ = + create_test_func( + wasm_mod, + "float64_bits_test", + Literal.float64_bits(0x4012000000000000L), + ); // 4.5 + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("float32", () => { + let wasm_mod = Module.create(); + let _ = create_test_func(wasm_mod, "float32_test", Literal.float32(3.5)); + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("float64", () => { + let wasm_mod = Module.create(); + let _ = create_test_func(wasm_mod, "float64_test", Literal.float64(5.5)); + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); +}); diff --git a/test/test.expected b/test/test.expected index 0b2a02ed..72519ea5 100644 --- a/test/test.expected +++ b/test/test.expected @@ -46,7 +46,62 @@ Running suite: Function ) ) . Running test: set_debug_location -{"version":3,"sources":[],"names":[],"mappings":"uBASoB,C"}Running suite: Module +{"version":3,"sources":[],"names":[],"mappings":"uBASoB,C"}Running suite: Literal +. Running test: int32 +(module + (type $0 (func)) + (func $int32_test + (drop + (i32.const 42) + ) + ) +) +. Running test: int64 +(module + (type $0 (func)) + (func $int64_test + (drop + (i64.const -35) + ) + ) +) +. Running test: float32_bits +(module + (type $0 (func)) + (func $float32_bits_test + (drop + (f32.const 4.5) + ) + ) +) +. Running test: float64_bits +(module + (type $0 (func)) + (func $float64_bits_test + (drop + (f64.const 4.5) + ) + ) +) +. Running test: float32 +(module + (type $0 (func)) + (func $float32_test + (drop + (f32.const 3.5) + ) + ) +) +. Running test: float64 +(module + (type $0 (func)) + (func $float64_test + (drop + (f64.const 5.5) + ) + ) +) +Running suite: Module . Running test: create and dispose . Running test: add custom section . Running test: write diff --git a/test/test.re b/test/test.re index 57d0ea0c..ebff3b73 100644 --- a/test/test.re +++ b/test/test.re @@ -1,8 +1,8 @@ open Module_test; +open Literal_test; open Function_test; open Settings_test; open Type_test; -// TODO: Type // TODO: Table // TODO: Struct_Type // TODO: Signature_Type @@ -10,7 +10,6 @@ open Type_test; // TODO: Packed_Type // TODO: OP // TODO: Memory -// TODO: Literal // TODO: Import // TODO: Heap_Type // TODO: Global From a0b87378310ee665a07b1dbea7790c804a1c83e5 Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Sat, 6 Dec 2025 18:58:55 -0500 Subject: [PATCH 05/11] feat: Document `type.mli` --- src/type.mli | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/type.mli b/src/type.mli index 874ddb37..e015b3f4 100644 --- a/src/type.mli +++ b/src/type.mli @@ -1,25 +1,83 @@ type t +(** Represents a Binaryen wasm type. *) val none : t +(** The none type, e.g., [void.] *) + val int32 : t +(** The 32-bit integer type. *) + val int64 : t +(** The 64-bit integer type. *) + val float32 : t +(** The 32-bit float type. *) + val float64 : t +(** The 64-bit float type. *) + val vec128 : t +(** The 128-bit vector type. *) + val funcref : t +(** The function reference type. *) + val anyref : t +(** The any reference type. *) + val eqref : t +(** The eq reference type. *) + val i31ref : t +(** The i31 reference type. *) + val structref : t +(** The struct reference type. *) + val arrayref : t +(** The array reference type. *) + val stringref : t +(** The string reference type. *) + val nullref : t +(** The null reference type. *) + val null_externref : t +(** The null external reference type. *) + val null_funcref : t +(** The null function reference type. *) + val unreachable : t +(** A special type indicating unreachable code when obtaining information about + an expression. *) + val auto : t +(** A special type used in [Expression.Block] exclusively. Lets the API figure + out a block's result type automatically. *) + val create : t array -> t +(** Creates a multi-value type from an array of types. + @param Types The array of types + @return The multi-value type *) + val expand : t -> t array +(** Expands a multi-value type to an array of types. + @param type The multi-value type + @return The types in the multi-value type *) + val is_nullable : t -> bool +(** Weather the given type is nullable. + @param type The type to check + @return [true] if the type is nullable, [false] otherwise *) + val from_heap_type : Heap_type.t -> t +(** Produces a regular type from the given heap type. + @param heap_type The heap type + @return The corresponding type *) + val get_heap_type : t -> Heap_type.t +(** Gets the heap type of the given type. + @param type The type + @return The corresponding heap type *) From 5d7b2b08eca09974bf760869ba4f3d3bc5542847 Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Sat, 6 Dec 2025 19:30:14 -0500 Subject: [PATCH 06/11] feat: Implement tests for `Global` --- .gitignore | 7 ++- .vscode/extensions.json | 5 ++ test/global_test.re | 103 ++++++++++++++++++++++++++++++++++++++++ test/test.expected | 16 ++++++- test/test.re | 59 +++++++---------------- 5 files changed, 147 insertions(+), 43 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 test/global_test.re diff --git a/.gitignore b/.gitignore index a13a0c0a..f4c12ddc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,15 @@ +# Esy node_modules/ _esy/ _build/ + +# Ocaml .merlin + +# General META -.vscode/ .DS_Store *.tar.gz *.install *.zip + diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..a6b9faba --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "ocamllabs.ocaml-platform" + ] +} \ No newline at end of file diff --git a/test/global_test.re b/test/global_test.re new file mode 100644 index 00000000..42d23919 --- /dev/null +++ b/test/global_test.re @@ -0,0 +1,103 @@ +open Binaryen; +open Frame_work; + +suite("Global", () => { + let create_test_global = (wasm_mod, name) => { + Global.add_global( + wasm_mod, + name, + Type.int32, + false, + Expression.Const.make(wasm_mod, Literal.int32(0l)), + ); + }; + test("Add global", () => { + let wasm_mod = Module.create(); + let _ = create_test_global(wasm_mod, "test_global"); + Printf.printf("%s\n", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("Get Name", () => { + let wasm_mod = Module.create(); + let name = "test_global"; + let test_global = create_test_global(wasm_mod, name); + assert(Global.get_name(test_global) == name); + Module.dispose(wasm_mod); + }); + test("Get Type", () => { + let wasm_mod = Module.create(); + let test_global = create_test_global(wasm_mod, "test_non_null"); + assert(!Type.is_nullable(Global.get_type(test_global))); + let test_global = + Global.add_global( + wasm_mod, + "test_null", + Type.nullref, + false, + Expression.Null.make(), + ); + assert(Type.is_nullable(Global.get_type(test_global))); + Module.dispose(wasm_mod); + }); + test("Get Global", () => { + let wasm_mod = Module.create(); + let name = "test_global"; + let _ = create_test_global(wasm_mod, name); + let test_global = Global.get_global(wasm_mod, name); + assert(Global.get_name(test_global) == name); + Module.dispose(wasm_mod); + }); + test("Is Mutable", () => { + let wasm_mod = Module.create(); + let test_global = create_test_global(wasm_mod, "test_global"); + assert(!Global.is_mutable(test_global)); + let mutable_global = + Global.add_global( + wasm_mod, + "mutable_global", + Type.int32, + true, + Expression.Const.make(wasm_mod, Literal.int32(42l)), + ); + assert(Global.is_mutable(mutable_global)); + Module.dispose(wasm_mod); + }); + test("Number of Globals", () => { + let wasm_mod = Module.create(); + assert(Global.get_num_globals(wasm_mod) == 0); + let _ = create_test_global(wasm_mod, "global1"); + let _ = create_test_global(wasm_mod, "global2"); + assert(Global.get_num_globals(wasm_mod) == 2); + Module.dispose(wasm_mod); + }); + test("Remove Global", () => { + let wasm_mod = Module.create(); + let _ = create_test_global(wasm_mod, "global_to_remove"); + assert(Global.get_num_globals(wasm_mod) == 1); + Global.remove_global(wasm_mod, "global_to_remove"); + assert(Global.get_num_globals(wasm_mod) == 0); + Module.dispose(wasm_mod); + }); + test("Get Global by Index", () => { + let wasm_mod = Module.create(); + let name1 = "global1"; + let name2 = "global2"; + let _ = create_test_global(wasm_mod, name1); + let _ = create_test_global(wasm_mod, name2); + let global1 = Global.get_global_by_index(wasm_mod, 0); + let global2 = Global.get_global_by_index(wasm_mod, 1); + assert(Global.get_name(global1) == name1); + assert(Global.get_name(global2) == name2); + Module.dispose(wasm_mod); + }); + test("Get Init Expr", () => { + let wasm_mod = Module.create(); + let test_global = create_test_global(wasm_mod, "test_global"); + let init_expr = Global.get_init_expr(test_global); + switch (Expression.get_kind(init_expr)) { + | Expression.Const => assert(true) + | _ => assert(false) + }; + Module.dispose(wasm_mod); + }); +}); diff --git a/test/test.expected b/test/test.expected index 72519ea5..64e45753 100644 --- a/test/test.expected +++ b/test/test.expected @@ -46,7 +46,21 @@ Running suite: Function ) ) . Running test: set_debug_location -{"version":3,"sources":[],"names":[],"mappings":"uBASoB,C"}Running suite: Literal +{"version":3,"sources":[],"names":[],"mappings":"uBASoB,C"}Running suite: Global +. Running test: Add global +(module + (global $test_global i32 (i32.const 0)) +) + +. Running test: Get Name +. Running test: Get Type +. Running test: Get Global +. Running test: Is Mutable +. Running test: Number of Globals +. Running test: Remove Global +. Running test: Get Global by Index +. Running test: Get Init Expr +Running suite: Literal . Running test: int32 (module (type $0 (func)) diff --git a/test/test.re b/test/test.re index ebff3b73..c4031cb2 100644 --- a/test/test.re +++ b/test/test.re @@ -1,33 +1,22 @@ -open Module_test; -open Literal_test; -open Function_test; -open Settings_test; +open Module_test; // TODO: Document +open Literal_test; // TODO: Document +open Function_test; // TODO: Document +open Settings_test; // TODO: Document open Type_test; -// TODO: Table -// TODO: Struct_Type -// TODO: Signature_Type -// TODO: Passes -// TODO: Packed_Type -// TODO: OP -// TODO: Memory -// TODO: Import -// TODO: Heap_Type -// TODO: Global -// TODO: Expression -// TODO: Export -// TODO: Element_Segment -// TODO: Array_Type - -// let wasm_mod = Module.create () -// let _ = Module.set_features wasm_mod [ Module.Feature.all ] -// let import_wasm_mod = Module.create () - -// (* Testing pass_argument *) -// let _ = assert (Settings.get_pass_argument "theKey" = None) -// let _ = Settings.set_pass_argument "theKey" "theValue" -// let _ = assert (Settings.get_pass_argument "theKey" = Some "theValue") -// let _ = Settings.set_pass_argument "theKey" "theValue2" -// let _ = assert (Settings.get_pass_argument "theKey" = Some "theValue2") +// TODO: Heap_Type (quick) +// TODO: Array_Type (quick) +// TODO: Struct_Type (quick) +// TODO: Signature_Type (quick) +// TODO: Packed_Type (quick) +open Global_test; // TODO: Document +// TODO: Memory (Medium) +// TODO: Table (Medium) +// TODO: Element_Segment (Medium) +// TODO: Export (Medium) +// TODO: Import (Medium) +// TODO: Passes (Hard) +// TODO: Expression (Hard) +// TODO: OP (Medium) // let _ = // Import.add_memory_import import_wasm_mod "internal_name" "external_name" @@ -142,18 +131,6 @@ open Type_test; // let _ = Expression.print table_grow -// let _ = -// Global.add_global wasm_mod "max_int64" Type.int64 false -// (Expression.Const.make wasm_mod (Literal.int64 Int64.max_int)) - -// let _ = -// Global.add_global wasm_mod "max_int64_mut" Type.int64 true -// (Expression.Const.make wasm_mod (Literal.int64 Int64.max_int)) - -// let _ = -// Global.add_global wasm_mod "test_float64_bits" Type.float64 false -// (Expression.Const.make wasm_mod (Literal.float64_bits 0x3FF3AE147AE147AEL)) - // let _ = // Table.add_active_element_segment wasm_mod "table" "elem" [ "adder" ] // (Expression.Const.make wasm_mod (Literal.int32 0l)) From 0680c434f6b316a5344969a503290fea897bfe11 Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Sat, 6 Dec 2025 20:16:41 -0500 Subject: [PATCH 07/11] feat: Fix deprecated ocaml binding --- src/module.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/module.js b/src/module.js index 00167c72..d6bf5b0b 100644 --- a/src/module.js +++ b/src/module.js @@ -122,11 +122,11 @@ function caml_binaryen_module_get_debug_info_filename(wasm_mod, index) { } //Provides: caml_binaryen_add_custom_section -//Requires: caml_jsstring_of_string, caml_array_of_string +//Requires: caml_jsstring_of_string, caml_uint8_array_of_string function caml_binaryen_add_custom_section(wasm_mod, name, contents) { return wasm_mod.addCustomSection( caml_jsstring_of_string(name), - caml_array_of_string(contents) + caml_uint8_array_of_string(contents) ); } From c5ae894de8a18e1cc7edecc4e5fb5cc115bc1dac Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Sat, 13 Dec 2025 13:26:41 -0500 Subject: [PATCH 08/11] chore: debug js test failure --- test/module_test.re | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/module_test.re b/test/module_test.re index 3b39cab5..4fd7f2e6 100644 --- a/test/module_test.re +++ b/test/module_test.re @@ -29,6 +29,10 @@ suite("Module", () => { assert(source_map == None); assert(bytes == Bytes.of_string("\000asm\001\000\000\000")); let (_, source_map) = Module.write(wasm_mod, Some("")); + switch (source_map) { + | None => assert(false) + | Some(map) => Printf.printf("%s\n", map); + } assert( source_map == Some({|{"version":3,"sources":[],"names":[],"mappings":""}|}), From be733829c081a27bd0194e9f1de7e0599cb7055e Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Sat, 13 Dec 2025 13:52:55 -0500 Subject: [PATCH 09/11] feat: Test Memory --- src/memory.c | 6 ++- src/memory.mli | 1 + test/memory_test.re | 108 ++++++++++++++++++++++++++++++++++++++++++++ test/module_test.re | 4 -- test/test.expected | 16 +++++++ test/test.re | 2 +- 6 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 test/memory_test.re diff --git a/src/memory.c b/src/memory.c index 6e8f8990..56076b0c 100644 --- a/src/memory.c +++ b/src/memory.c @@ -121,7 +121,11 @@ caml_binaryen_get_memory_segment_byte_offset(value _module, value _name) { CAMLparam2(_module, _name); BinaryenModuleRef module = BinaryenModuleRef_val(_module); char* name = Safe_String_val(_name); - CAMLreturn(Val_int(BinaryenGetMemorySegmentByteOffset(module, name))); + if (BinaryenGetMemorySegmentPassive(module, name)) { + CAMLreturn(Val_int(0)); + } else { + CAMLreturn(Val_int(BinaryenGetMemorySegmentByteOffset(module, name))); + } } CAMLprim value diff --git a/src/memory.mli b/src/memory.mli index 6ecf64d8..06bfb5fe 100644 --- a/src/memory.mli +++ b/src/memory.mli @@ -11,6 +11,7 @@ val set_memory : bool -> string -> unit +(** module, initial, maximum, exportName, segmentNames, segmentData, segmentPassive, segmentOffsets, segmentSizes, shared, memory64, memoryName *) val has_memory : Module.t -> bool val get_initial : Module.t -> string -> int diff --git a/test/memory_test.re b/test/memory_test.re new file mode 100644 index 00000000..49deabfb --- /dev/null +++ b/test/memory_test.re @@ -0,0 +1,108 @@ +open Binaryen; +open Frame_work; + +suite("Memory", () => { + test("set_memory", () => { + let wasm_mod = Module.create(); + Memory.set_memory(wasm_mod, 1, 2, "memory", [], false, false, "memory"); + Printf.printf("%s", Module.write_text(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("Has Memory", () => { + let wasm_mod = Module.create(); + assert(!Memory.has_memory(wasm_mod)); + Memory.set_memory(wasm_mod, 1, 2, "hasMemory1", [], false, false, "hasMemory1"); + assert(Memory.has_memory(wasm_mod)); + Module.dispose(wasm_mod); + }); + test("Get Initial", () => { + let wasm_mod = Module.create(); + Memory.set_memory(wasm_mod, 3, 5, "getInitial1", [], false, false, "getInitial1"); + assert(Memory.get_initial(wasm_mod, "getInitial1") == 3); + Module.dispose(wasm_mod); + }); + test("Has Max", () => { + let wasm_mod = Module.create(); + Memory.set_memory(wasm_mod, 3, 5, "hasMax1", [], false, false, "hasMax1"); + assert(Memory.has_max(wasm_mod, "hasMax1")); + Memory.set_memory(wasm_mod, 3, Memory.unlimited, "hasMax2", [], false, false, "hasMax2"); + assert(!Memory.has_max(wasm_mod, "hasMax2")); + Module.dispose(wasm_mod); + }); + test("Get Max", () => { + let wasm_mod = Module.create(); + Memory.set_memory(wasm_mod, 3, 5, "getMax1", [], false, false, "getMax1"); + assert(Memory.get_max(wasm_mod, "getMax1") == 5); + Module.dispose(wasm_mod); + }); + test("Get Shared", () => { + let wasm_mod = Module.create(); + Memory.set_memory(wasm_mod, 3, 5, "getShared1", [], true, false, "getShared1"); + assert(Memory.is_shared(wasm_mod, "getShared1")); + Memory.set_memory(wasm_mod, 3, 5, "getShared2", [], false, false, "getShared2"); + assert(!Memory.is_shared(wasm_mod, "getShared2")); + Module.dispose(wasm_mod); + }); + test("Get Is64", () => { + let wasm_mod = Module.create(); + Memory.set_memory(wasm_mod, 3, 5, "is64_1", [], false, true, "is64_1"); + assert(Memory.is_64(wasm_mod, "is64_1")); + Memory.set_memory(wasm_mod, 3, 5, "is64_2", [], false, false, "is64_2"); + assert(!Memory.is_64(wasm_mod, "is64_2")); + Module.dispose(wasm_mod); + }); + // Segments + let create_segment = (wasm_mod, name, kind): Memory.segment => { + let data = Bytes.of_string(Printf.sprintf("data_%s", name)); + { + name, + data, + kind, + size: Bytes.length(data), + }; + }; + test("Get Num Segments", () => { + let wasm_mod = Module.create(); + Memory.set_memory(wasm_mod, 1, 2, "getNumSegs1", [], false, false, "getNumSegs1"); + assert(Memory.get_num_segments(wasm_mod) == 0); + Memory.set_memory(wasm_mod, 1, 2, "getNumSegs2", [ + create_segment(wasm_mod, "1", Memory.Passive), + create_segment(wasm_mod, "2", Memory.Passive) + ], false, false, "getNumSegs2"); + assert(Memory.get_num_segments(wasm_mod) == 2); + Module.dispose(wasm_mod); + }); + test("Get Segment Offset", () => { + let wasm_mod = Module.create(); + Memory.set_memory(wasm_mod, 1, 2, "getOffset1", [ + create_segment(wasm_mod, "1", Memory.Passive) + ], false, false, "getOffset1"); + assert(Memory.get_segment_byte_offset(wasm_mod, "1") == 0); + Memory.set_memory(wasm_mod, 1, 2, "getOffset2", [ + create_segment(wasm_mod, "2", Memory.Active({ offset: Expression.Const.make(wasm_mod, Literal.int32(4l)) })) + ], false, false, "getOffset2"); + assert(Memory.get_segment_byte_offset(wasm_mod, "2") == 4); + Module.dispose(wasm_mod); + }); + test("Get Segment Passive", () => { + let wasm_mod = Module.create(); + Memory.set_memory(wasm_mod, 1, 2, "getPassive1", [ + create_segment(wasm_mod, "1", Memory.Passive) + ], false, false, "getPassive1"); + assert(Memory.get_segment_passive(wasm_mod, "1")); + Memory.set_memory(wasm_mod, 1, 2, "getPassive2", [ + create_segment(wasm_mod, "2", Memory.Active({ offset: Expression.Const.make(wasm_mod, Literal.int32(4l)) })) + ], false, false, "getPassive2"); + assert(!Memory.get_segment_passive(wasm_mod, "2")); + Module.dispose(wasm_mod); + }); + test("Get Segment Data", () => { + let wasm_mod = Module.create(); + Memory.set_memory(wasm_mod, 1, 2, "getData1", [ + create_segment(wasm_mod, "1", Memory.Passive) + ], false, false, "getData1"); + let data = Memory.get_segment_data(wasm_mod, "1"); + assert(Bytes.to_string(data) == "data_1"); + Module.dispose(wasm_mod); + }); +}); diff --git a/test/module_test.re b/test/module_test.re index 4fd7f2e6..3b39cab5 100644 --- a/test/module_test.re +++ b/test/module_test.re @@ -29,10 +29,6 @@ suite("Module", () => { assert(source_map == None); assert(bytes == Bytes.of_string("\000asm\001\000\000\000")); let (_, source_map) = Module.write(wasm_mod, Some("")); - switch (source_map) { - | None => assert(false) - | Some(map) => Printf.printf("%s\n", map); - } assert( source_map == Some({|{"version":3,"sources":[],"names":[],"mappings":""}|}), diff --git a/test/test.expected b/test/test.expected index 64e45753..58dc61ee 100644 --- a/test/test.expected +++ b/test/test.expected @@ -115,6 +115,22 @@ Running suite: Literal ) ) ) +Running suite: Memory +. Running test: set_memory +(module + (memory $memory 1 2) + (export "memory" (memory $memory)) +) +. Running test: Has Memory +. Running test: Get Initial +. Running test: Has Max +. Running test: Get Max +. Running test: Get Shared +. Running test: Get Is64 +. Running test: Get Num Segments +. Running test: Get Segment Offset +. Running test: Get Segment Passive +. Running test: Get Segment Data Running suite: Module . Running test: create and dispose . Running test: add custom section diff --git a/test/test.re b/test/test.re index c4031cb2..52e58653 100644 --- a/test/test.re +++ b/test/test.re @@ -9,7 +9,7 @@ open Type_test; // TODO: Signature_Type (quick) // TODO: Packed_Type (quick) open Global_test; // TODO: Document -// TODO: Memory (Medium) +open Memory_test; // TODO: Document // TODO: Table (Medium) // TODO: Element_Segment (Medium) // TODO: Export (Medium) From 07fbebb6255726f19c885d69f7e4ce82283c1d39 Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Sat, 13 Dec 2025 13:57:31 -0500 Subject: [PATCH 10/11] feat: Correct Memory --- src/memory.c | 2 +- src/memory.mli | 4 +- test/memory_test.re | 180 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 151 insertions(+), 35 deletions(-) diff --git a/src/memory.c b/src/memory.c index 56076b0c..b4e9971d 100644 --- a/src/memory.c +++ b/src/memory.c @@ -122,7 +122,7 @@ caml_binaryen_get_memory_segment_byte_offset(value _module, value _name) { BinaryenModuleRef module = BinaryenModuleRef_val(_module); char* name = Safe_String_val(_name); if (BinaryenGetMemorySegmentPassive(module, name)) { - CAMLreturn(Val_int(0)); + CAMLreturn(Val_int(-1)); } else { CAMLreturn(Val_int(BinaryenGetMemorySegmentByteOffset(module, name))); } diff --git a/src/memory.mli b/src/memory.mli index 06bfb5fe..2fcb4c80 100644 --- a/src/memory.mli +++ b/src/memory.mli @@ -11,7 +11,9 @@ val set_memory : bool -> string -> unit -(** module, initial, maximum, exportName, segmentNames, segmentData, segmentPassive, segmentOffsets, segmentSizes, shared, memory64, memoryName *) +(** module, initial, maximum, exportName, segmentNames, segmentData, + segmentPassive, segmentOffsets, segmentSizes, shared, memory64, memoryName +*) val has_memory : Module.t -> bool val get_initial : Module.t -> string -> int diff --git a/test/memory_test.re b/test/memory_test.re index 49deabfb..ad1e680b 100644 --- a/test/memory_test.re +++ b/test/memory_test.re @@ -6,42 +6,87 @@ suite("Memory", () => { let wasm_mod = Module.create(); Memory.set_memory(wasm_mod, 1, 2, "memory", [], false, false, "memory"); Printf.printf("%s", Module.write_text(wasm_mod)); - Module.dispose(wasm_mod); + Module.dispose(wasm_mod); }); test("Has Memory", () => { let wasm_mod = Module.create(); assert(!Memory.has_memory(wasm_mod)); - Memory.set_memory(wasm_mod, 1, 2, "hasMemory1", [], false, false, "hasMemory1"); + Memory.set_memory( + wasm_mod, + 1, + 2, + "hasMemory1", + [], + false, + false, + "hasMemory1", + ); assert(Memory.has_memory(wasm_mod)); - Module.dispose(wasm_mod); + Module.dispose(wasm_mod); }); test("Get Initial", () => { let wasm_mod = Module.create(); - Memory.set_memory(wasm_mod, 3, 5, "getInitial1", [], false, false, "getInitial1"); + Memory.set_memory( + wasm_mod, + 3, + 5, + "getInitial1", + [], + false, + false, + "getInitial1", + ); assert(Memory.get_initial(wasm_mod, "getInitial1") == 3); - Module.dispose(wasm_mod); + Module.dispose(wasm_mod); }); test("Has Max", () => { let wasm_mod = Module.create(); Memory.set_memory(wasm_mod, 3, 5, "hasMax1", [], false, false, "hasMax1"); assert(Memory.has_max(wasm_mod, "hasMax1")); - Memory.set_memory(wasm_mod, 3, Memory.unlimited, "hasMax2", [], false, false, "hasMax2"); + Memory.set_memory( + wasm_mod, + 3, + Memory.unlimited, + "hasMax2", + [], + false, + false, + "hasMax2", + ); assert(!Memory.has_max(wasm_mod, "hasMax2")); - Module.dispose(wasm_mod); + Module.dispose(wasm_mod); }); test("Get Max", () => { let wasm_mod = Module.create(); Memory.set_memory(wasm_mod, 3, 5, "getMax1", [], false, false, "getMax1"); assert(Memory.get_max(wasm_mod, "getMax1") == 5); - Module.dispose(wasm_mod); + Module.dispose(wasm_mod); }); test("Get Shared", () => { let wasm_mod = Module.create(); - Memory.set_memory(wasm_mod, 3, 5, "getShared1", [], true, false, "getShared1"); + Memory.set_memory( + wasm_mod, + 3, + 5, + "getShared1", + [], + true, + false, + "getShared1", + ); assert(Memory.is_shared(wasm_mod, "getShared1")); - Memory.set_memory(wasm_mod, 3, 5, "getShared2", [], false, false, "getShared2"); + Memory.set_memory( + wasm_mod, + 3, + 5, + "getShared2", + [], + false, + false, + "getShared2", + ); assert(!Memory.is_shared(wasm_mod, "getShared2")); - Module.dispose(wasm_mod); + Module.dispose(wasm_mod); }); test("Get Is64", () => { let wasm_mod = Module.create(); @@ -49,7 +94,7 @@ suite("Memory", () => { assert(Memory.is_64(wasm_mod, "is64_1")); Memory.set_memory(wasm_mod, 3, 5, "is64_2", [], false, false, "is64_2"); assert(!Memory.is_64(wasm_mod, "is64_2")); - Module.dispose(wasm_mod); + Module.dispose(wasm_mod); }); // Segments let create_segment = (wasm_mod, name, kind): Memory.segment => { @@ -63,44 +108,113 @@ suite("Memory", () => { }; test("Get Num Segments", () => { let wasm_mod = Module.create(); - Memory.set_memory(wasm_mod, 1, 2, "getNumSegs1", [], false, false, "getNumSegs1"); + Memory.set_memory( + wasm_mod, + 1, + 2, + "getNumSegs1", + [], + false, + false, + "getNumSegs1", + ); assert(Memory.get_num_segments(wasm_mod) == 0); - Memory.set_memory(wasm_mod, 1, 2, "getNumSegs2", [ - create_segment(wasm_mod, "1", Memory.Passive), - create_segment(wasm_mod, "2", Memory.Passive) - ], false, false, "getNumSegs2"); + Memory.set_memory( + wasm_mod, + 1, + 2, + "getNumSegs2", + [ + create_segment(wasm_mod, "1", Memory.Passive), + create_segment(wasm_mod, "2", Memory.Passive), + ], + false, + false, + "getNumSegs2", + ); assert(Memory.get_num_segments(wasm_mod) == 2); Module.dispose(wasm_mod); }); test("Get Segment Offset", () => { let wasm_mod = Module.create(); - Memory.set_memory(wasm_mod, 1, 2, "getOffset1", [ - create_segment(wasm_mod, "1", Memory.Passive) - ], false, false, "getOffset1"); - assert(Memory.get_segment_byte_offset(wasm_mod, "1") == 0); - Memory.set_memory(wasm_mod, 1, 2, "getOffset2", [ - create_segment(wasm_mod, "2", Memory.Active({ offset: Expression.Const.make(wasm_mod, Literal.int32(4l)) })) - ], false, false, "getOffset2"); + Memory.set_memory( + wasm_mod, + 1, + 2, + "getOffset1", + [create_segment(wasm_mod, "1", Memory.Passive)], + false, + false, + "getOffset1", + ); + assert(Memory.get_segment_byte_offset(wasm_mod, "1") == (-1)); + Memory.set_memory( + wasm_mod, + 1, + 2, + "getOffset2", + [ + create_segment( + wasm_mod, + "2", + Memory.Active({ + offset: Expression.Const.make(wasm_mod, Literal.int32(4l)), + }), + ), + ], + false, + false, + "getOffset2", + ); assert(Memory.get_segment_byte_offset(wasm_mod, "2") == 4); Module.dispose(wasm_mod); }); test("Get Segment Passive", () => { let wasm_mod = Module.create(); - Memory.set_memory(wasm_mod, 1, 2, "getPassive1", [ - create_segment(wasm_mod, "1", Memory.Passive) - ], false, false, "getPassive1"); + Memory.set_memory( + wasm_mod, + 1, + 2, + "getPassive1", + [create_segment(wasm_mod, "1", Memory.Passive)], + false, + false, + "getPassive1", + ); assert(Memory.get_segment_passive(wasm_mod, "1")); - Memory.set_memory(wasm_mod, 1, 2, "getPassive2", [ - create_segment(wasm_mod, "2", Memory.Active({ offset: Expression.Const.make(wasm_mod, Literal.int32(4l)) })) - ], false, false, "getPassive2"); + Memory.set_memory( + wasm_mod, + 1, + 2, + "getPassive2", + [ + create_segment( + wasm_mod, + "2", + Memory.Active({ + offset: Expression.Const.make(wasm_mod, Literal.int32(4l)), + }), + ), + ], + false, + false, + "getPassive2", + ); assert(!Memory.get_segment_passive(wasm_mod, "2")); Module.dispose(wasm_mod); }); test("Get Segment Data", () => { let wasm_mod = Module.create(); - Memory.set_memory(wasm_mod, 1, 2, "getData1", [ - create_segment(wasm_mod, "1", Memory.Passive) - ], false, false, "getData1"); + Memory.set_memory( + wasm_mod, + 1, + 2, + "getData1", + [create_segment(wasm_mod, "1", Memory.Passive)], + false, + false, + "getData1", + ); let data = Memory.get_segment_data(wasm_mod, "1"); assert(Bytes.to_string(data) == "data_1"); Module.dispose(wasm_mod); From 21f9ff789bdfc90920c9f9fedd9114749f868431 Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Sat, 13 Dec 2025 21:42:49 -0500 Subject: [PATCH 11/11] chore: log failing test --- test/memory_test.re | 3 ++- test/test.expected | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/memory_test.re b/test/memory_test.re index ad1e680b..52956016 100644 --- a/test/memory_test.re +++ b/test/memory_test.re @@ -147,7 +147,8 @@ suite("Memory", () => { false, "getOffset1", ); - assert(Memory.get_segment_byte_offset(wasm_mod, "1") == (-1)); + Printf.printf("%d\n", Memory.get_segment_byte_offset(wasm_mod, "1")); + assert(Memory.get_segment_byte_offset(wasm_mod, "1") == -1); Memory.set_memory( wasm_mod, 1, diff --git a/test/test.expected b/test/test.expected index 58dc61ee..950f8786 100644 --- a/test/test.expected +++ b/test/test.expected @@ -129,6 +129,7 @@ Running suite: Memory . Running test: Get Is64 . Running test: Get Num Segments . Running test: Get Segment Offset +-1 . Running test: Get Segment Passive . Running test: Get Segment Data Running suite: Module