diff --git a/cli/bin/grain.js b/cli/bin/grain.js index 103f6ccf8d..02da38eb26 100755 --- a/cli/bin/grain.js +++ b/cli/bin/grain.js @@ -146,6 +146,10 @@ class GrainCommand extends commander.Command { "--use-start-section", "replaces the _start export with a start section during linking" ); + cmd.forwardOption( + "--export-function-table", + "causes the function table to be exported under the given name" + ); cmd.forwardOption( "--no-pervasives", "don't automatically import the Grain Pervasives module" diff --git a/compiler/src/linking/link.re b/compiler/src/linking/link.re index 5748245c1e..c4b3999939 100644 --- a/compiler/src/linking/link.re +++ b/compiler/src/linking/link.re @@ -698,6 +698,17 @@ let link_all = (linked_mod, dependencies, signature) => { Type.funcref, ); + switch (Grain_utils.Config.export_function_table^) { + | Some(name) => + ignore @@ + Export.add_table_export( + linked_mod, + Comp_utils.grain_global_function_table, + name, + ) + | None => () + }; + let (initial_memory, maximum_memory) = switch (Config.initial_memory_pages^, Config.maximum_memory_pages^) { | (initial_memory, Some(maximum_memory)) => ( diff --git a/compiler/src/utils/config.re b/compiler/src/utils/config.re index d0405fde4c..6716d46b63 100644 --- a/compiler/src/utils/config.re +++ b/compiler/src/utils/config.re @@ -545,6 +545,14 @@ let use_start_section = false, ); +let export_function_table = + opt( + ~names=["export-function-table"], + ~conv=option_conv(Cmdliner.Arg.string), + ~doc="Causes the function table to be exported under the given name.", + None, + ); + let elide_type_info = toggle_flag( ~names=["elide-type-info"], diff --git a/compiler/src/utils/config.rei b/compiler/src/utils/config.rei index 03c69dcd4c..ebb9749efd 100644 --- a/compiler/src/utils/config.rei +++ b/compiler/src/utils/config.rei @@ -40,6 +40,9 @@ let wasi_polyfill: ref(option(string)); let use_start_section: ref(bool); +/** Weather to export the function table under a specifed name */ +let export_function_table: ref(option(string)); + /** Compilation profile, e.g. release for production builds */ let profile: ref(option(profile)); diff --git a/compiler/test/suites/linking.re b/compiler/test/suites/linking.re index 175ff747ba..d3fd8074f7 100644 --- a/compiler/test/suites/linking.re +++ b/compiler/test/suites/linking.re @@ -122,6 +122,35 @@ describe("linking", ({test, testSkip}) => { ); }); + test("export_function_table", ({expect}) => { + let name = "export_function_table"; + let outfile = wasmfile(name); + ignore @@ + compile( + ~config_fn= + () => {Grain_utils.Config.export_function_table := Some("funcTable")}, + name, + {|module Test; print("Hello, world!")|}, + ); + let ic = open_in_bin(outfile); + let sections = Grain_utils.Wasm_utils.get_wasm_sections(ic); + close_in(ic); + let export_sections = + List.find_map( + (sec: Grain_utils.Wasm_utils.wasm_bin_section) => + switch (sec) { + | {sec_type: Export(exports)} => Some(exports) + | _ => None + }, + sections, + ); + expect.option(export_sections).toBeSome(); + expect.list(Option.get(export_sections)).toContainEqual( + ~equals=tuple_equal, + (WasmTable, "funcTable"), + ); + }); + test("import_memory", ({expect}) => { let name = "import_memory"; let outfile = wasmfile(name);