├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── Makefile ├── README.md ├── examples ├── hello.gr ├── net.gr ├── simpleProcess.gr ├── sleep.gr ├── spawn.gr └── version.gr ├── lunatic.gr └── src ├── api ├── distributed.gr ├── error.gr ├── message.gr ├── networking.gr ├── process.gr ├── registry.gr ├── timer.gr ├── version.gr └── wasi.gr ├── net.gr ├── process.gr └── version.gr /.gitignore: -------------------------------------------------------------------------------- 1 | *.wasm 2 | *.wat 3 | .vscode/*.log 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "grain.cliFlags": "-I . " 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Grain 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | examples: 2 | make hello 3 | make net 4 | make sleep 5 | make spawn 6 | make version 7 | 8 | hello: 9 | grain compile -I . --use-start-section --no-wasm-tail-call --release examples/hello.gr 10 | net: 11 | grain compile -I . --use-start-section --no-wasm-tail-call --release examples/net.gr 12 | sleep: 13 | grain compile -I . --use-start-section --no-wasm-tail-call --release examples/sleep.gr 14 | spawn: 15 | grain compile -I . --use-start-section --no-wasm-tail-call --release examples/spawn.gr 16 | version: 17 | grain compile -I . --use-start-section --no-wasm-tail-call --release examples/version.gr 18 | simpleProcess: 19 | grain compile -I . --use-start-section --no-wasm-tail-call --release examples/simpleProcess.gr 20 | 21 | .PHONY: examples hello net sleep spawn version 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lunatic-gr 2 | 3 | Grain bindings to [Lunatic](https://lunatic.solutions). 4 | 5 | ## Buidling the examples 6 | 7 | With the latest version of Grain on your PATH, run `make examples`. The examples can be run via the `lunatic` command: 8 | 9 | ```sh 10 | lunatic examples/hello.gr.wasm 11 | ``` 12 | -------------------------------------------------------------------------------- /examples/hello.gr: -------------------------------------------------------------------------------- 1 | module Hello 2 | 3 | provide let _start = () => { 4 | print("Hello from Lunatic!") 5 | } 6 | -------------------------------------------------------------------------------- /examples/net.gr: -------------------------------------------------------------------------------- 1 | module Net 2 | 3 | from "lunatic" include Lunatic 4 | use Lunatic.{ module Net } 5 | 6 | provide let _start = () => { 7 | match (Net.DNS.resolve("google.com:80")) { 8 | Ok(addresses) => print(addresses), 9 | Err(err) => print(err), 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/simpleProcess.gr: -------------------------------------------------------------------------------- 1 | module Main 2 | 3 | from "lunatic" include Lunatic 4 | use Lunatic.{ module Process } 5 | 6 | @externalName("_start") 7 | provide let main = () => { 8 | print("[main process] Hello from the main process!") 9 | print("[main process] Spawning a child...") 10 | 11 | Process.spawn(() => { 12 | print("[subprocess] 👋 from spawned process!") 13 | }) 14 | 15 | void 16 | } 17 | 18 | provide let __lunatic_bootstrap = Process.__lunatic_bootstrap 19 | -------------------------------------------------------------------------------- /examples/sleep.gr: -------------------------------------------------------------------------------- 1 | module Sleep 2 | 3 | from "lunatic" include Lunatic 4 | use Lunatic.{ module Process } 5 | 6 | provide let _start = () => { 7 | print("Counting...") 8 | 9 | for (let mut i = 0; i < 10; i += 1) { 10 | Process.sleep(200L) 11 | print(i) 12 | } 13 | 14 | print("Done.") 15 | } 16 | -------------------------------------------------------------------------------- /examples/spawn.gr: -------------------------------------------------------------------------------- 1 | module Spawn 2 | 3 | from "bytes" include Bytes 4 | from "int64" include Int64 5 | from "result" include Result 6 | 7 | from "lunatic" include Lunatic 8 | use Lunatic.{ module Process } 9 | 10 | record SomeData { 11 | key: String, 12 | value: a, 13 | } 14 | 15 | let mailbox = Process.createMailbox() 16 | 17 | provide let _start = () => { 18 | let localdata = { key: "localdata", value: 54321 } 19 | 20 | print("[main process] Hello from the main process!") 21 | print("[main process] Spawning a child...") 22 | 23 | let child = Result.unwrap(Process.spawn(() => { 24 | print("[subprocess] 👋 from spawned process!") 25 | print("[subprocess] receiving some data...") 26 | print(Result.unwrap(Process.receive(mailbox))) 27 | print("[subprocess] data within the closure:") 28 | print(localdata) 29 | })) 30 | 31 | print("[main process] sending some data...") 32 | 33 | Process.send(child, { key: "datakey", value: 42 }, mailbox) 34 | Process.sleep(100L) 35 | } 36 | 37 | provide let __lunatic_bootstrap = Process.__lunatic_bootstrap 38 | -------------------------------------------------------------------------------- /examples/version.gr: -------------------------------------------------------------------------------- 1 | module Version 2 | 3 | from "lunatic" include Lunatic 4 | use Lunatic.{ module Version } 5 | 6 | let semverString = (major, minor, patch) => { 7 | let (major, minor, patch) = ( 8 | toString(major), 9 | toString(minor), 10 | toString(patch), 11 | ) 12 | "v" ++ major ++ "." ++ minor ++ "." ++ patch 13 | } 14 | 15 | provide let _start = () => { 16 | print( 17 | "Hello from Lunatic " ++ 18 | semverString(Version.major(), Version.minor(), Version.patch()) 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /lunatic.gr: -------------------------------------------------------------------------------- 1 | module Lunatic 2 | 3 | from "./src/net.gr" include Net 4 | from "./src/process.gr" include Process 5 | from "./src/version.gr" include Version 6 | 7 | provide { module Net, module Process, module Version } 8 | -------------------------------------------------------------------------------- /src/api/distributed.gr: -------------------------------------------------------------------------------- 1 | module Distributed 2 | 3 | provide foreign wasm get_nodes: ( 4 | WasmI32, 5 | WasmI32, 6 | ) -> WasmI32 as getNodes from "lunatic::distributed" 7 | provide foreign wasm nodes_count: () -> WasmI32 as nodesCount from "lunatic::distributed" 8 | provide foreign wasm node_id: () -> WasmI64 as nodeId from "lunatic::distributed" 9 | provide foreign wasm module_id: () -> WasmI64 as moduleId from "lunatic::distributed" 10 | provide foreign wasm send: ( 11 | WasmI64, 12 | WasmI64, 13 | ) -> Void as send from "lunatic::distributed" 14 | provide foreign wasm send_receive_skip_search: ( 15 | WasmI64, 16 | WasmI64, 17 | WasmI64, 18 | ) -> WasmI32 as sendReceiveSkipSearch from "lunatic::distributed" 19 | provide foreign wasm spawn: ( 20 | WasmI64, 21 | WasmI64, 22 | WasmI64, 23 | WasmI32, 24 | WasmI32, 25 | WasmI32, 26 | WasmI32, 27 | WasmI32, 28 | ) -> WasmI32 as spawn from "lunatic::distributed" 29 | -------------------------------------------------------------------------------- /src/api/error.gr: -------------------------------------------------------------------------------- 1 | module Error 2 | 3 | provide foreign wasm string_size: WasmI64 -> WasmI32 as stringSize from "lunatic::error" 4 | provide foreign wasm to_string: ( 5 | WasmI64, 6 | WasmI32, 7 | ) -> Void as toString from "lunatic::error" 8 | provide foreign wasm drop: WasmI64 -> Void as drop from "lunatic::error" 9 | -------------------------------------------------------------------------------- /src/api/message.gr: -------------------------------------------------------------------------------- 1 | module Message 2 | 3 | provide foreign wasm create_data: ( 4 | WasmI64, 5 | WasmI64, 6 | ) -> Void as createData from "lunatic::message" 7 | provide foreign wasm write_data: ( 8 | WasmI32, 9 | WasmI32, 10 | ) -> WasmI32 as writeData from "lunatic::message" 11 | provide foreign wasm read_data: ( 12 | WasmI32, 13 | WasmI32, 14 | ) -> WasmI32 as readData from "lunatic::message" 15 | provide foreign wasm seek_data: WasmI64 -> Void as seekData from "lunatic::message" 16 | provide foreign wasm get_tag: () -> WasmI64 as getTag from "lunatic::message" 17 | provide foreign wasm data_size: () -> WasmI64 as dataSize from "lunatic::message" 18 | provide foreign wasm push_module: WasmI64 -> WasmI64 as pushModule from "lunatic::message" 19 | provide foreign wasm take_module: WasmI64 -> WasmI64 as takeModule from "lunatic::message" 20 | provide foreign wasm push_tcp_stream: WasmI64 -> WasmI64 as pushTcpStream from "lunatic::message" 21 | provide foreign wasm take_tcp_stream: WasmI64 -> WasmI64 as takeTcpStream from "lunatic::message" 22 | provide foreign wasm push_tls_stream: WasmI64 -> WasmI64 as pushTlsStream from "lunatic::message" 23 | provide foreign wasm take_tls_stream: WasmI64 -> WasmI64 as takeTlsStream from "lunatic::message" 24 | provide foreign wasm send: WasmI64 -> WasmI32 as send from "lunatic::message" 25 | provide foreign wasm send_receive_skip_search: ( 26 | WasmI64, 27 | WasmI64, 28 | ) -> WasmI32 as sendReceiveSkipSearch from "lunatic::message" 29 | provide foreign wasm receive: ( 30 | WasmI32, 31 | WasmI32, 32 | WasmI64, 33 | ) -> WasmI32 as receive from "lunatic::message" 34 | -------------------------------------------------------------------------------- /src/api/networking.gr: -------------------------------------------------------------------------------- 1 | module Networking 2 | 3 | provide foreign wasm resolve: ( 4 | WasmI32, 5 | WasmI32, 6 | WasmI64, 7 | WasmI32, 8 | ) -> WasmI32 from "lunatic::networking" 9 | provide foreign wasm drop_dns_iterator: WasmI64 -> Void as dropDnsIterator from "lunatic::networking" 10 | provide foreign wasm resolve_next: ( 11 | WasmI64, 12 | WasmI32, 13 | WasmI32, 14 | WasmI32, 15 | WasmI32, 16 | WasmI32, 17 | ) -> WasmI32 as resolveNext from "lunatic::networking" 18 | provide foreign wasm tcp_bind: ( 19 | WasmI32, 20 | WasmI32, 21 | WasmI32, 22 | WasmI32, 23 | WasmI32, 24 | WasmI32, 25 | ) -> WasmI32 as tcpBind from "lunatic::networking" 26 | provide foreign wasm udp_bind: ( 27 | WasmI32, 28 | WasmI32, 29 | WasmI32, 30 | WasmI32, 31 | WasmI32, 32 | WasmI32, 33 | ) -> WasmI32 as udpBind from "lunatic::networking" 34 | provide foreign wasm drop_tcp_listener: WasmI64 -> Void as dropTcpListener from "lunatic::networking" 35 | provide foreign wasm drop_tls_listener: WasmI64 -> Void as dropTlsListener from "lunatic::networking" 36 | provide foreign wasm drop_udp_socket: WasmI64 -> Void as dropUdpSocket from "lunatic::networking" 37 | provide foreign wasm tcp_local_addr: ( 38 | WasmI64, 39 | WasmI32, 40 | ) -> WasmI32 as tcpLocalAddr from "lunatic::networking" 41 | provide foreign wasm tls_local_addr: ( 42 | WasmI64, 43 | WasmI32, 44 | ) -> WasmI32 as tlsLocalAddr from "lunatic::networking" 45 | provide foreign wasm udp_local_addr: ( 46 | WasmI64, 47 | WasmI32, 48 | ) -> WasmI32 as udpLocalAddr from "lunatic::networking" 49 | provide foreign wasm tcp_accept: ( 50 | WasmI64, 51 | WasmI32, 52 | WasmI32, 53 | ) -> WasmI32 as tcpAccept from "lunatic::networking" 54 | provide foreign wasm tcp_connect: ( 55 | WasmI32, 56 | WasmI32, 57 | WasmI32, 58 | WasmI32, 59 | WasmI32, 60 | WasmI64, 61 | WasmI32, 62 | ) -> WasmI32 as tcpConnect from "lunatic::networking" 63 | provide foreign wasm udp_connect: ( 64 | WasmI64, 65 | WasmI32, 66 | WasmI32, 67 | WasmI32, 68 | WasmI32, 69 | WasmI32, 70 | WasmI64, 71 | WasmI32, 72 | ) -> WasmI32 as udpConnect from "lunatic::networking" 73 | provide foreign wasm drop_tcp_stream: WasmI64 -> Void as dropTcpStream from "lunatic::networking" 74 | provide foreign wasm clone_tcp_stream: WasmI64 -> WasmI64 as cloneTcpStream from "lunatic::networking" 75 | provide foreign wasm tcp_write_vectored: ( 76 | WasmI64, 77 | WasmI32, 78 | WasmI32, 79 | WasmI32, 80 | ) -> WasmI32 as tcpWriteVectored from "lunatic::networking" 81 | provide foreign wasm tcp_read: ( 82 | WasmI64, 83 | WasmI32, 84 | WasmI32, 85 | WasmI32, 86 | ) -> WasmI32 as tcpRead from "lunatic::networking" 87 | provide foreign wasm tcp_peek: ( 88 | WasmI64, 89 | WasmI32, 90 | WasmI32, 91 | WasmI32, 92 | ) -> WasmI32 as tcpPeek from "lunatic::networking" 93 | provide foreign wasm udp_send: ( 94 | WasmI64, 95 | WasmI32, 96 | WasmI32, 97 | WasmI32, 98 | ) -> WasmI32 as udpSend from "lunatic::networking" 99 | provide foreign wasm udp_send_to: ( 100 | WasmI64, 101 | WasmI32, 102 | WasmI32, 103 | WasmI32, 104 | WasmI32, 105 | WasmI32, 106 | WasmI32, 107 | WasmI32, 108 | WasmI32, 109 | ) -> WasmI32 as udpSendTo from "lunatic::networking" 110 | provide foreign wasm udp_receive: ( 111 | WasmI64, 112 | WasmI32, 113 | WasmI32, 114 | WasmI32, 115 | ) -> WasmI32 as udpReceive from "lunatic::networking" 116 | provide foreign wasm udp_receive_from: ( 117 | WasmI64, 118 | WasmI32, 119 | WasmI32, 120 | WasmI32, 121 | WasmI32, 122 | ) -> WasmI32 as udpReceiveFrom from "lunatic::networking" 123 | provide foreign wasm set_udp_socket_ttl: ( 124 | WasmI64, 125 | WasmI32, 126 | ) -> Void as setUdpSocketTtl from "lunatic::networking" 127 | provide foreign wasm set_udp_socket_broadcast: ( 128 | WasmI64, 129 | WasmI32, 130 | ) -> Void as setUdpSocketBroadcast from "lunatic::networking" 131 | provide foreign wasm get_udp_socket_ttl: WasmI64 -> WasmI32 as getUdpSocketTtl from "lunatic::networking" 132 | provide foreign wasm get_udp_socket_broadcast: WasmI64 -> WasmI32 as getUdpSocketBroadcast from "lunatic::networking" 133 | provide foreign wasm clone_udp_socket: WasmI64 -> WasmI64 as cloneUdpSocket from "lunatic::networking" 134 | provide foreign wasm tcp_flush: ( 135 | WasmI64, 136 | WasmI32, 137 | ) -> WasmI32 as tcpFlush from "lunatic::networking" 138 | provide foreign wasm tls_flush: ( 139 | WasmI64, 140 | WasmI32, 141 | ) -> WasmI32 as tlsFlush from "lunatic::networking" 142 | provide foreign wasm set_read_timeout: ( 143 | WasmI64, 144 | WasmI64, 145 | ) -> Void as setReadTimeout from "lunatic::networking" 146 | provide foreign wasm get_read_timeout: WasmI64 -> WasmI64 as getReadTimeout from "lunatic::networking" 147 | provide foreign wasm set_write_timeout: ( 148 | WasmI64, 149 | WasmI64, 150 | ) -> Void as setWriteTimeout from "lunatic::networking" 151 | provide foreign wasm get_write_timeout: WasmI64 -> WasmI64 as getWriteTimeout from "lunatic::networking" 152 | provide foreign wasm set_peek_timeout: ( 153 | WasmI64, 154 | WasmI64, 155 | ) -> Void as setPeekTimeout from "lunatic::networking" 156 | provide foreign wasm get_peek_timeout: WasmI64 -> WasmI64 as getPeekTimeout from "lunatic::networking" 157 | provide foreign wasm tls_bind: ( 158 | WasmI32, 159 | WasmI32, 160 | WasmI32, 161 | WasmI32, 162 | WasmI32, 163 | WasmI32, 164 | WasmI32, 165 | WasmI32, 166 | WasmI32, 167 | WasmI32, 168 | ) -> WasmI32 as tlsBind from "lunatic::networking" 169 | provide foreign wasm tls_accept: ( 170 | WasmI64, 171 | WasmI32, 172 | WasmI32, 173 | ) -> WasmI32 as tlsAccept from "lunatic::networking" 174 | provide foreign wasm tls_connect: ( 175 | WasmI32, 176 | WasmI32, 177 | WasmI32, 178 | WasmI64, 179 | WasmI32, 180 | WasmI32, 181 | WasmI32, 182 | ) -> WasmI32 as tlsConnect from "lunatic::networking" 183 | provide foreign wasm drop_tls_stream: WasmI64 -> Void as dropTlsStream from "lunatic::networking" 184 | provide foreign wasm clone_tls_stream: WasmI64 -> WasmI64 as cloneTlsStream from "lunatic::networking" 185 | provide foreign wasm tls_write_vectored: ( 186 | WasmI64, 187 | WasmI32, 188 | WasmI32, 189 | WasmI32, 190 | ) -> WasmI32 as tlsWriteVectored from "lunatic::networking" 191 | provide foreign wasm tls_read: ( 192 | WasmI64, 193 | WasmI32, 194 | WasmI32, 195 | WasmI32, 196 | ) -> WasmI32 as tlsRead from "lunatic::networking" 197 | provide foreign wasm set_tls_read_timeout: ( 198 | WasmI64, 199 | WasmI64, 200 | ) -> Void as setTlsReadTimeout from "lunatic::networking" 201 | provide foreign wasm get_tls_read_timeout: WasmI64 -> WasmI64 as getTlsReadTimeout from "lunatic::networking" 202 | provide foreign wasm set_tls_write_timeout: ( 203 | WasmI64, 204 | WasmI64, 205 | ) -> Void as setTlsWriteTimeout from "lunatic::networking" 206 | provide foreign wasm get_tls_write_timeout: WasmI64 -> WasmI64 as getTlsWriteTimeout from "lunatic::networking" 207 | -------------------------------------------------------------------------------- /src/api/process.gr: -------------------------------------------------------------------------------- 1 | module Process 2 | 3 | provide foreign wasm compile_module: ( 4 | WasmI32, 5 | WasmI32, 6 | WasmI32, 7 | ) -> WasmI32 as compileModule from "lunatic::process" 8 | provide foreign wasm drop_module: WasmI64 -> Void as dropModule from "lunatic::process" 9 | provide foreign wasm create_config: () -> WasmI64 as createConfig from "lunatic::process" 10 | provide foreign wasm drop_config: WasmI64 -> Void as dropConfig from "lunatic::process" 11 | provide foreign wasm config_set_max_memory: ( 12 | WasmI64, 13 | WasmI64, 14 | ) -> Void as configSetMaxMemory from "lunatic::process" 15 | provide foreign wasm config_get_max_memory: WasmI64 -> WasmI64 as configGetMaxMemory from "lunatic::process" 16 | provide foreign wasm config_set_max_fuel: ( 17 | WasmI64, 18 | WasmI64, 19 | ) -> Void as configSetMaxFuel from "lunatic::process" 20 | provide foreign wasm config_get_max_fuel: WasmI64 -> WasmI64 as configGetMaxFuel from "lunatic::process" 21 | provide foreign wasm config_can_compile_modules: WasmI64 -> WasmI32 as configCanCompileModules from "lunatic::process" 22 | provide foreign wasm config_set_can_compile_modules: ( 23 | WasmI64, 24 | WasmI32, 25 | ) -> Void as configSetCanCompileModules from "lunatic::process" 26 | provide foreign wasm config_can_create_configs: WasmI64 -> WasmI32 as configCanCreateConfigs from "lunatic::process" 27 | provide foreign wasm config_set_can_create_configs: ( 28 | WasmI64, 29 | WasmI32, 30 | ) -> Void as configSetCanCreateConfigs from "lunatic::process" 31 | provide foreign wasm config_can_spawn_processes: WasmI64 -> WasmI32 as configCanSpawnProcesses from "lunatic::process" 32 | provide foreign wasm config_set_can_spawn_processes: ( 33 | WasmI64, 34 | WasmI32, 35 | ) -> Void as configSetCanSpawnProcesses from "lunatic::process" 36 | provide foreign wasm spawn: ( 37 | WasmI64, 38 | WasmI64, 39 | WasmI64, 40 | WasmI32, 41 | WasmI32, 42 | WasmI32, 43 | WasmI32, 44 | WasmI32, 45 | ) -> WasmI32 as spawn from "lunatic::process" 46 | provide foreign wasm sleep_ms: WasmI64 -> Void as sleepMs from "lunatic::process" 47 | provide foreign wasm die_when_link_dies: WasmI32 -> Void as dieWhenLinkDies from "lunatic::process" 48 | provide foreign wasm process_id: () -> WasmI64 as processId from "lunatic::process" 49 | provide foreign wasm link: ( 50 | WasmI64, 51 | WasmI64, 52 | ) -> Void as link from "lunatic::process" 53 | provide foreign wasm unlink: WasmI64 -> Void as unlink from "lunatic::process" 54 | provide foreign wasm kill: WasmI64 -> Void as kill from "lunatic::process" 55 | -------------------------------------------------------------------------------- /src/api/registry.gr: -------------------------------------------------------------------------------- 1 | module Registry 2 | 3 | provide foreign wasm put: ( 4 | WasmI32, 5 | WasmI32, 6 | WasmI64, 7 | WasmI64, 8 | ) -> Void as put from "lunatic::registry" 9 | provide foreign wasm get: ( 10 | WasmI32, 11 | WasmI32, 12 | WasmI32, 13 | WasmI32, 14 | ) -> WasmI32 as get from "lunatic::registry" 15 | provide foreign wasm remove: ( 16 | WasmI32, 17 | WasmI32, 18 | ) -> Void as remove from "lunatic::registry" 19 | -------------------------------------------------------------------------------- /src/api/timer.gr: -------------------------------------------------------------------------------- 1 | module Timer 2 | 3 | provide foreign wasm send_after: ( 4 | WasmI64, 5 | WasmI64, 6 | ) -> WasmI64 as sendAfter from "lunatic::timer" 7 | provide foreign wasm cancel_timer: WasmI64 -> WasmI32 as cancelTimer from "lunatic::timer" 8 | -------------------------------------------------------------------------------- /src/api/version.gr: -------------------------------------------------------------------------------- 1 | module Version 2 | 3 | provide foreign wasm major: () -> WasmI32 from "lunatic::version" 4 | provide foreign wasm minor: () -> WasmI32 from "lunatic::version" 5 | provide foreign wasm patch: () -> WasmI32 from "lunatic::version" 6 | -------------------------------------------------------------------------------- /src/api/wasi.gr: -------------------------------------------------------------------------------- 1 | module Wasi 2 | 3 | provide foreign wasm config_add_environment_variable: ( 4 | WasmI64, 5 | WasmI32, 6 | WasmI32, 7 | WasmI32, 8 | WasmI32, 9 | ) -> Void as configAddEnvironmentVariable from "lunatic::wasi" 10 | provide foreign wasm config_add_command_line_argument: ( 11 | WasmI64, 12 | WasmI32, 13 | WasmI32, 14 | ) -> Void as configAddCommandLineArgument from "lunatic::wasi" 15 | provide foreign wasm config_preopen_dir: ( 16 | WasmI64, 17 | WasmI32, 18 | WasmI32, 19 | ) -> Void as configPreopenDir from "lunatic::wasi" 20 | -------------------------------------------------------------------------------- /src/net.gr: -------------------------------------------------------------------------------- 1 | module Net 2 | 3 | from "pervasives" include Pervasives 4 | from "runtime/unsafe/conv" include Conv 5 | from "runtime/unsafe/wasmi32" include WasmI32 6 | from "runtime/unsafe/wasmi64" include WasmI64 7 | from "runtime/unsafe/memory" include Memory 8 | from "runtime/dataStructures" include DataStructures 9 | from "runtime/numberUtils" include NumberUtils 10 | from "bigint" include BigInt 11 | from "string" include String 12 | 13 | from "./api/message.gr" include Message 14 | from "./api/networking.gr" include Networking 15 | 16 | provide enum IPType { 17 | IPv4(Uint32), 18 | IPv6(BigInt), 19 | } 20 | 21 | provide record IPAddress { 22 | address: IPType, 23 | port: Uint16, 24 | flowInfo: Uint32, 25 | scopeId: Uint32, 26 | } 27 | 28 | primitive magic = "@magic" 29 | 30 | provide module DNS { 31 | use Networking.{ resolve, resolveNext, dropDnsIterator } 32 | 33 | @unsafe 34 | let ipPointer = Memory.malloc(30n) 35 | @unsafe 36 | let _ADDRESS_OFFSET = 0n 37 | @unsafe 38 | let _TYPE_OFFSET = 16n 39 | @unsafe 40 | let _PORT_OFFSET = 20n 41 | @unsafe 42 | let _FLOW_INFO_OFFSET = 22n 43 | @unsafe 44 | let _SCOPE_ID_OFFSET = 26n 45 | 46 | @unsafe 47 | let resolveDNSIterator = id => { 48 | let add = (+) 49 | use WasmI32.{ (+) } 50 | 51 | let mut addresses = [] 52 | 53 | while ( 54 | WasmI32.eqz( 55 | resolveNext( 56 | id, 57 | ipPointer + _TYPE_OFFSET, 58 | ipPointer + _ADDRESS_OFFSET, 59 | ipPointer + _PORT_OFFSET, 60 | ipPointer + _FLOW_INFO_OFFSET, 61 | ipPointer + _SCOPE_ID_OFFSET 62 | ) 63 | ) 64 | ) { 65 | let address = match (WasmI32.load(ipPointer, _TYPE_OFFSET)) { 66 | 4n => { 67 | use WasmI32.{ (<<), (|) } 68 | 69 | // These bytes are big endian, but WebAssembly is little endian 70 | 71 | let mut ip = 0n 72 | ip = ip | WasmI32.load8U(ipPointer, _ADDRESS_OFFSET) << 24n 73 | ip = ip | WasmI32.load8U(ipPointer, _ADDRESS_OFFSET + 1n) << 16n 74 | ip = ip | WasmI32.load8U(ipPointer, _ADDRESS_OFFSET + 2n) << 8n 75 | ip = ip | WasmI32.load8U(ipPointer, _ADDRESS_OFFSET + 3n) 76 | 77 | IPv4(WasmI32.toGrain(DataStructures.newInt32(ip))) 78 | }, 79 | 6n => { 80 | use WasmI64.{ (<<), (|) } 81 | 82 | // These bytes are big endian, but WebAssembly is little endian 83 | 84 | let mut high = 0N 85 | high = high | WasmI64.load8U(ipPointer, _ADDRESS_OFFSET) << 56N 86 | high = high | WasmI64.load8U(ipPointer, _ADDRESS_OFFSET + 1n) << 48N 87 | high = high | WasmI64.load8U(ipPointer, _ADDRESS_OFFSET + 2n) << 40N 88 | high = high | WasmI64.load8U(ipPointer, _ADDRESS_OFFSET + 3n) << 32N 89 | high = high | WasmI64.load8U(ipPointer, _ADDRESS_OFFSET + 4n) << 24N 90 | high = high | WasmI64.load8U(ipPointer, _ADDRESS_OFFSET + 5n) << 16N 91 | high = high | WasmI64.load8U(ipPointer, _ADDRESS_OFFSET + 6n) << 8N 92 | high = high | WasmI64.load8U(ipPointer, _ADDRESS_OFFSET + 7n) 93 | 94 | let mut low = 0N 95 | low = low | WasmI64.load8U(ipPointer, _ADDRESS_OFFSET + 8n) << 56N 96 | low = low | WasmI64.load8U(ipPointer, _ADDRESS_OFFSET + 9n) << 48N 97 | low = low | WasmI64.load8U(ipPointer, _ADDRESS_OFFSET + 10n) << 40N 98 | low = low | WasmI64.load8U(ipPointer, _ADDRESS_OFFSET + 11n) << 32N 99 | low = low | WasmI64.load8U(ipPointer, _ADDRESS_OFFSET + 12n) << 24N 100 | low = low | WasmI64.load8U(ipPointer, _ADDRESS_OFFSET + 13n) << 16N 101 | low = low | WasmI64.load8U(ipPointer, _ADDRESS_OFFSET + 14n) << 8N 102 | low = low | WasmI64.load8U(ipPointer, _ADDRESS_OFFSET + 15n) 103 | 104 | let high = WasmI32.toGrain(DataStructures.newInt64(high)): Number 105 | let low = WasmI32.toGrain(DataStructures.newInt64(low)): Number 106 | 107 | IPv6(BigInt.fromNumber(add(high * 0x100000000, low))) 108 | }, 109 | _ => fail "unknown IP type", 110 | } 111 | 112 | let port = DataStructures.tagUint16( 113 | WasmI32.load16U(ipPointer, _PORT_OFFSET) 114 | ) 115 | 116 | let flowInfo = WasmI32.toGrain( 117 | DataStructures.newUint32(WasmI32.load(ipPointer, _FLOW_INFO_OFFSET)) 118 | ): Uint32 119 | 120 | let scopeId = WasmI32.toGrain( 121 | DataStructures.newUint32(WasmI32.load(ipPointer, _SCOPE_ID_OFFSET)) 122 | ): Uint32 123 | 124 | let address = { address, port, flowInfo, scopeId } 125 | 126 | addresses = [address, ...addresses] 127 | } 128 | 129 | dropDnsIterator(id) 130 | 131 | addresses 132 | } 133 | 134 | @unsafe 135 | let _ID_PTR = Memory.malloc(8n) 136 | 137 | @unsafe 138 | provide let resolveTimeout = (host: String, timeout: Int64) => { 139 | use WasmI32.{ (+) } 140 | let hostPtr = WasmI32.fromGrain(host) + 8n 141 | let hostSize = WasmI32.load(WasmI32.fromGrain(host), 4n) 142 | 143 | let timeout = WasmI64.load(WasmI32.fromGrain(timeout), 8n) 144 | 145 | let result = resolve(hostPtr, hostSize, timeout, _ID_PTR) 146 | 147 | if (WasmI32.eqz(result)) { 148 | let id = WasmI64.load(_ID_PTR, 0n) 149 | Ok(resolveDNSIterator(id)) 150 | } else { 151 | match (result) { 152 | 9027n => Err("timed out"), 153 | _ => Err("unknown error"), 154 | } 155 | } 156 | } 157 | 158 | provide let resolve = (host: String) => { 159 | resolveTimeout(host, -1L) 160 | } 161 | } 162 | 163 | provide module Tcp { 164 | abstract type TcpListener = Int64 165 | abstract type TcpStream = Int64 166 | 167 | provide enum TcpError { 168 | FailedToResolveBindAddress, 169 | FailedToBind, 170 | FailedToAcceptConnection, 171 | TcpTimeout, 172 | TcpSocketClosed, 173 | TcpFlushFailed, 174 | } 175 | 176 | @unsafe 177 | let _ID_PTR = Memory.malloc(8n) 178 | @unsafe 179 | let _DNS_ITERATOR_PTR = Memory.malloc(8n) 180 | 181 | @unsafe 182 | provide let bind = address => { 183 | use WasmI32.{ (+) } 184 | 185 | let addresses = DNS.resolve(address) 186 | let ip = match (addresses) { 187 | Ok([ip, ..._]) => ip, 188 | _ => return Err(FailedToResolveBindAddress), 189 | } 190 | 191 | let mut ipType = 0n 192 | let mut ipPtr = 0n 193 | match (ip.address) { 194 | IPv4(addr) => { 195 | ipType = 4n 196 | ipPtr = WasmI32.load(WasmI32.fromGrain(addr), 4n) 197 | }, 198 | IPv6(addr) => { 199 | ipType = 6n 200 | ipPtr = WasmI32.fromGrain(addr) + 16n 201 | }, 202 | } 203 | 204 | let port = DataStructures.untagUint16(ip.port) 205 | let flowInfo = WasmI32.load(WasmI32.fromGrain(ip.flowInfo), 4n) 206 | let scopeId = WasmI32.load(WasmI32.fromGrain(ip.scopeId), 4n) 207 | 208 | let err = Networking.tcpBind( 209 | ipType, 210 | ipPtr, 211 | port, 212 | flowInfo, 213 | scopeId, 214 | _ID_PTR 215 | ) 216 | ignore(ip) 217 | 218 | return if (WasmI32.eqz(err)) { 219 | Ok( 220 | WasmI32.toGrain(DataStructures.newUint64(WasmI64.load(_ID_PTR, 0n))): 221 | TcpListener, 222 | ) 223 | } else { 224 | Err(FailedToBind) 225 | } 226 | } 227 | 228 | @unsafe 229 | provide let accept = (listener: TcpListener) => { 230 | let listenerId = WasmI64.load(WasmI32.fromGrain(listener), 8n) 231 | 232 | let err = Networking.tcpAccept(listenerId, _ID_PTR, _DNS_ITERATOR_PTR) 233 | return if (WasmI32.eqz(err)) { 234 | Ok( 235 | WasmI32.toGrain(DataStructures.newUint64(WasmI64.load(_ID_PTR, 0n))): 236 | TcpStream, 237 | ) 238 | } else { 239 | Err(FailedToAcceptConnection) 240 | } 241 | } 242 | 243 | @unsafe 244 | let _BYTES_AFFECTED_PTR = Memory.malloc(8n) 245 | @unsafe 246 | let _SINGLE_IOV = Memory.malloc(8n) 247 | 248 | @unsafe 249 | provide let read = (stream: TcpStream, buffer: Bytes) => { 250 | use WasmI32.{ (+) } 251 | 252 | let streamId = WasmI64.load(WasmI32.fromGrain(stream), 8n) 253 | let mut bufPtr = WasmI32.fromGrain(buffer) 254 | let bufSize = WasmI32.load(bufPtr, 4n) 255 | bufPtr += 8n 256 | 257 | let err = Networking.tcpRead(streamId, bufPtr, bufSize, _BYTES_AFFECTED_PTR) 258 | match (err) { 259 | 0n => { 260 | let bytesRead = WasmI32.wrapI64(WasmI64.load(_BYTES_AFFECTED_PTR, 0n)) 261 | Ok(DataStructures.tagSimpleNumber(bytesRead)) 262 | }, 263 | 1n => Err(TcpSocketClosed), 264 | _ => Err(TcpTimeout), 265 | } 266 | } 267 | 268 | @unsafe 269 | provide let write = (stream: TcpStream, buffer: Bytes) => { 270 | use WasmI32.{ (+) } 271 | 272 | let streamId = WasmI64.load(WasmI32.fromGrain(stream), 8n) 273 | let mut bufPtr = WasmI32.fromGrain(buffer) 274 | let bufSize = WasmI32.load(bufPtr, 4n) 275 | bufPtr += 8n 276 | 277 | WasmI32.store(_SINGLE_IOV, bufPtr, 0n) 278 | WasmI32.store(_SINGLE_IOV, bufSize, 4n) 279 | 280 | let err = Networking.tcpWriteVectored( 281 | streamId, 282 | _SINGLE_IOV, 283 | 1n, 284 | _BYTES_AFFECTED_PTR 285 | ) 286 | match (err) { 287 | 0n => { 288 | let bytesWritten = WasmI32.wrapI64( 289 | WasmI64.load(_BYTES_AFFECTED_PTR, 0n) 290 | ) 291 | Ok(DataStructures.tagSimpleNumber(bytesWritten)) 292 | }, 293 | 1n => Err(TcpSocketClosed), 294 | _ => Err(TcpTimeout), 295 | } 296 | } 297 | 298 | provide let writeString = (stream, string: String) => { 299 | write(stream, magic(string)) 300 | } 301 | 302 | @unsafe 303 | provide let clone = (stream: TcpStream) => { 304 | let streamId = WasmI64.load(WasmI32.fromGrain(stream), 8n) 305 | let newId = Networking.cloneTcpStream(streamId) 306 | WasmI32.toGrain(DataStructures.newUint64(newId)): TcpStream 307 | } 308 | 309 | @unsafe 310 | provide let flush = (stream: TcpStream) => { 311 | let streamId = WasmI64.load(WasmI32.fromGrain(stream), 8n) 312 | let err = Networking.tcpFlush(streamId, _ID_PTR) 313 | match (err) { 314 | 0n => Ok(void), 315 | _ => Err(TcpFlushFailed), 316 | } 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /src/process.gr: -------------------------------------------------------------------------------- 1 | module Process 2 | 3 | from "runtime/unsafe/wasmi32" include WasmI32 4 | from "runtime/unsafe/wasmi64" include WasmI64 5 | from "runtime/unsafe/conv" include Conv 6 | from "runtime/unsafe/memory" include Memory 7 | from "runtime/dataStructures" include DataStructures 8 | from "runtime/unsafe/tags" include Tags 9 | 10 | from "array" include Array 11 | from "bytes" include Bytes 12 | from "int64" include Int64 13 | from "marshal" include Marshal 14 | from "result" include Result 15 | 16 | from "./api/distributed.gr" include Distributed 17 | use Distributed.{ nodeId } 18 | from "./api/process.gr" include Process 19 | use Process.{ 20 | compileModule, 21 | dropModule, 22 | createConfig, 23 | dropConfig, 24 | configSetMaxMemory, 25 | configGetMaxMemory, 26 | configSetMaxFuel, 27 | configGetMaxFuel, 28 | configCanCompileModules, 29 | configSetCanCompileModules, 30 | configCanCreateConfigs, 31 | configSetCanCreateConfigs, 32 | configCanSpawnProcesses, 33 | configSetCanSpawnProcesses, 34 | spawn, 35 | sleepMs, 36 | dieWhenLinkDies, 37 | processId, 38 | link, 39 | unlink, 40 | kill, 41 | } 42 | from "./api/message.gr" include Message 43 | use Message.{ 44 | createData, 45 | writeData, 46 | readData, 47 | seekData, 48 | getTag, 49 | dataSize, 50 | send, 51 | receive, 52 | } 53 | 54 | from "./api/registry.gr" include Registry 55 | from "./net.gr" include Net 56 | 57 | provide record Process { 58 | nodeId: Int64, 59 | pid: Int64, 60 | } 61 | 62 | provide enum Resource { 63 | TcpStream(Net.Tcp.TcpStream), 64 | } 65 | 66 | provide exception LunaticError 67 | provide exception ReceiveTimeout 68 | provide exception MarshalError(String) 69 | 70 | abstract type Mailbox = Int64 71 | 72 | /* Tags 0 - 127 are reserved for special purposes. */ 73 | 74 | @unsafe 75 | let mut currentTag = 128N 76 | 77 | let bootstrapMailbox: Mailbox<() => Void> = 1L 78 | 79 | @unsafe 80 | provide let createMailbox = () => { 81 | use WasmI64.{ (+) } 82 | let tag = currentTag 83 | currentTag += 1N 84 | WasmI32.toGrain(DataStructures.newInt64(tag)): Mailbox 85 | } 86 | 87 | @unsafe 88 | let createData = (tag: Int64, capacity: Int64) => { 89 | createData( 90 | WasmI64.load(WasmI32.fromGrain(tag), 8n), 91 | WasmI64.load(WasmI32.fromGrain(capacity), 8n) 92 | ) 93 | ignore(tag) 94 | ignore(capacity) 95 | } 96 | 97 | @unsafe 98 | let writeData = (bytes: Bytes) => { 99 | use WasmI32.{ (+) } 100 | let bytesPtr = WasmI32.fromGrain(bytes) 101 | let bytesWritten = writeData(bytesPtr + 8n, WasmI32.load(bytesPtr, 4n)) 102 | WasmI32.toGrain(DataStructures.newInt32(bytesWritten)): Int32 103 | } 104 | 105 | @unsafe 106 | let readData = (bytes: Bytes) => { 107 | use WasmI32.{ (+) } 108 | let bytesPtr = WasmI32.fromGrain(bytes) 109 | let bytesRead = readData(bytesPtr + 8n, WasmI32.load(bytesPtr, 4n)) 110 | WasmI32.toGrain(DataStructures.newInt32(bytesRead)): Int32 111 | } 112 | 113 | @unsafe 114 | let seekData = (position: Int64) => { 115 | seekData(WasmI64.load(WasmI32.fromGrain(position), 8n)) 116 | } 117 | 118 | @unsafe 119 | let getTag = () => { 120 | WasmI32.toGrain(DataStructures.newInt64(getTag())): Int64 121 | } 122 | 123 | @unsafe 124 | let dataSize = () => { 125 | WasmI32.toGrain(DataStructures.newInt64(dataSize())): Int64 126 | } 127 | 128 | @unsafe 129 | let sendRaw = (process: Process) => { 130 | let err = send(WasmI64.load(WasmI32.fromGrain(process.pid), 8n)) 131 | ignore(process) 132 | if (WasmI32.eqz(err)) { 133 | Ok(void) 134 | } else { 135 | Err(LunaticError) 136 | } 137 | } 138 | 139 | @unsafe 140 | let receive = (tags: Option>, timeout: Uint64) => { 141 | use WasmI32.{ (*), (+) } 142 | let tags = match (tags) { 143 | Some(tags) => Array.fromList(tags), 144 | None => [>], 145 | } 146 | 147 | let count = DataStructures.untagSimpleNumber(Array.length(tags)) 148 | 149 | let tagsBuf = Memory.malloc(count * 8n) 150 | 151 | for (let mut i = 0n; WasmI32.ltU(i, count); i += 1n) { 152 | WasmI64.store( 153 | tagsBuf, 154 | WasmI64.load( 155 | WasmI32.fromGrain(tags[DataStructures.tagSimpleNumber(i)]), 156 | 8n 157 | ), 158 | i * 8n 159 | ) 160 | } 161 | 162 | let result = receive( 163 | tagsBuf, 164 | count, 165 | WasmI64.load(WasmI32.fromGrain(timeout), 8n) 166 | ) 167 | 168 | match (result) { 169 | 0n => { 170 | Ok(void) 171 | }, 172 | 9027n => Err(ReceiveTimeout), 173 | 1n => fail "can't recieve link failed message", 174 | _ => fail "unknown receive error", 175 | } 176 | } 177 | 178 | @unsafe 179 | let idPtr = Memory.malloc(8n) 180 | 181 | /** 182 | * Cause the current process to sleep for the given number of milliseconds. 183 | * 184 | * @param ms: The number of milliseconds to sleep 185 | */ 186 | @unsafe 187 | provide let sleep = (ms: Int64) => { 188 | let ms = WasmI64.load(WasmI32.fromGrain(ms), 8n) 189 | sleepMs(ms) 190 | } 191 | 192 | @unsafe 193 | provide let this = () => { 194 | let nodeId = Conv.toInt64(nodeId()) 195 | let pid = Conv.toInt64(processId()) 196 | { nodeId, pid }: Process 197 | } 198 | 199 | @unsafe 200 | provide let send: (Process, a, Mailbox) => Result = ( 201 | process, 202 | data, 203 | mailbox, 204 | ) => { 205 | let payload = Marshal.marshal(data) 206 | createData(mailbox, Int64.fromNumber(Bytes.length(payload))) 207 | writeData(payload) 208 | sendRaw(process) 209 | } 210 | 211 | provide let receive: (?timeout: Uint64, Mailbox) => Result = ( 212 | timeout=0xffffffffffffffffuL, 213 | mailbox, 214 | ) => { 215 | Result.flatMap((_) => { 216 | let size = dataSize() 217 | let buf = Bytes.make(Int64.toNumber(size)) 218 | readData(buf) 219 | Result.mapErr(msg => MarshalError(msg), Marshal.unmarshal(buf)) 220 | }, receive(Some([mailbox]), timeout)) 221 | } 222 | 223 | let tcpStreamMailbox = createMailbox(): Mailbox 224 | 225 | @unsafe 226 | provide let transferTcpStream = (process, stream) => { 227 | createData(tcpStreamMailbox, 8L) 228 | let streamId = WasmI64.load(WasmI32.fromGrain(stream), 8n) 229 | let index = WasmI32.toGrain( 230 | DataStructures.newUint64(Message.pushTcpStream(streamId)) 231 | ): Uint64 232 | let payload = Marshal.marshal(index) 233 | writeData(payload) 234 | sendRaw(process) 235 | } 236 | 237 | @unsafe 238 | provide let takeTcpStream = () => { 239 | Result.flatMap(index => { 240 | let index = WasmI64.load(WasmI32.fromGrain(index), 8n) 241 | let stream = WasmI32.toGrain( 242 | DataStructures.newUint64(Message.takeTcpStream(index)) 243 | ): Net.Tcp.TcpStream 244 | Ok(stream) 245 | }, receive(tcpStreamMailbox)) 246 | } 247 | 248 | @unsafe 249 | provide let spawn = (func: () => Void) => { 250 | use WasmI32.{ (+) } 251 | let funcName = WasmI32.fromGrain("__lunatic_bootstrap") 252 | 253 | let result = spawn( 254 | 0N, 255 | -1N, 256 | -1N, 257 | funcName + 8n, 258 | WasmI32.load(funcName, 4n), 259 | 0n, 260 | 0n, 261 | idPtr 262 | ) 263 | 264 | let spawnId = WasmI32.toGrain( 265 | DataStructures.newInt64(WasmI64.load(idPtr, 0n)) 266 | ) 267 | 268 | if (WasmI32.eqz(result)) { 269 | let process = { pid: spawnId, nodeId: Conv.toInt64(nodeId()) } 270 | match (send(process, func, bootstrapMailbox)) { 271 | Ok(_) => Ok(process), 272 | Err(_) => Err(spawnId: Int64), 273 | } 274 | } else { 275 | Err(spawnId: Int64) 276 | } 277 | } 278 | 279 | @unsafe 280 | provide let register = (name: String, process: Process) => { 281 | use WasmI32.{ (+) } 282 | let namePtr = WasmI32.fromGrain(name) 283 | let nameLen = WasmI32.load(namePtr, 4n) 284 | let namePtr = namePtr + 8n 285 | 286 | let nodeIdValue = WasmI64.load(WasmI32.fromGrain(process.nodeId), 8n) 287 | let processId = WasmI64.load(WasmI32.fromGrain(process.pid), 8n) 288 | 289 | Registry.put(namePtr, nameLen, nodeIdValue, processId) 290 | 291 | ignore(name) 292 | ignore(process) 293 | } 294 | 295 | @unsafe 296 | provide let lookup = (name: String) => { 297 | use WasmI32.{ (+) } 298 | let namePtr = WasmI32.fromGrain(name) 299 | let nameLen = WasmI32.load(namePtr, 4n) 300 | let namePtr = namePtr + 8n 301 | 302 | let nodeId = DataStructures.allocateInt64() 303 | let pid = DataStructures.allocateInt64() 304 | 305 | let err = Registry.get(namePtr, nameLen, nodeId + 8n, pid + 8n) 306 | if (WasmI32.eqz(err)) { 307 | Some({ nodeId: WasmI32.toGrain(nodeId), pid: WasmI32.toGrain(pid) }) 308 | } else { 309 | Memory.free(nodeId) 310 | Memory.free(pid) 311 | None 312 | } 313 | } 314 | 315 | @unsafe 316 | provide let deregister = (name: String) => { 317 | use WasmI32.{ (+) } 318 | let namePtr = WasmI32.fromGrain(name) 319 | let nameLen = WasmI32.load(namePtr, 4n) 320 | let namePtr = namePtr + 8n 321 | 322 | Registry.remove(namePtr, nameLen) 323 | 324 | ignore(name) 325 | } 326 | 327 | @unsafe 328 | provide let dieWhenLinkDies = shouldDie => { 329 | if (shouldDie) { 330 | dieWhenLinkDies(1n) 331 | } else { 332 | dieWhenLinkDies(0n) 333 | } 334 | } 335 | 336 | @unsafe 337 | provide let __lunatic_bootstrap = () => { 338 | // The mailbox type is not propagated within this module for some reason so 339 | // we use an explicit type annotation. 340 | // https://github.com/grain-lang/grain/issues/1356 341 | let result: Result<() => Void, Exception> = receive(bootstrapMailbox) 342 | match (result) { 343 | Ok(func) => func(), 344 | Err(ReceiveTimeout) => fail "didn't receive bootstrap message", 345 | Err(MarshalError(_)) => fail "corrupted bootsrap message", 346 | Err(_) => fail "unknown error", 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /src/version.gr: -------------------------------------------------------------------------------- 1 | module Version 2 | 3 | from "runtime/unsafe/wasmi32" include WasmI32 4 | from "runtime/dataStructures" include DataStructures 5 | use DataStructures.{ newInt32 } 6 | 7 | from "./api/version.gr" include Version 8 | use Version.{ major, minor, patch } 9 | 10 | @unsafe 11 | provide let major = () => { 12 | WasmI32.toGrain(newInt32(major())): Int32 13 | } 14 | 15 | @unsafe 16 | provide let minor = () => { 17 | WasmI32.toGrain(newInt32(minor())): Int32 18 | } 19 | 20 | @unsafe 21 | provide let patch = () => { 22 | WasmI32.toGrain(newInt32(patch())): Int32 23 | } 24 | --------------------------------------------------------------------------------