wasm
Alexander Bentkamp 1 year ago
parent 3ff46aeb54
commit 8d29761579

@ -1,75 +1,16 @@
const IO = {
_resolveGetLine: null,
_resolveRead: null,
_readPointer: null,
_nbytes: 0,
bufferStdIn : "",
putStrListeners: [],
listenPutStr(callback) {
this.putStrListeners.push(callback)
},
putStr(str) {
console.log('PUTSTR' + str)
str = str.split('\n')[2]
this.putStrListeners.forEach((listener) => {
listener(str)
})
},
async getLine() {
return new Promise((resolve, reject) => {
this._resolveGetLine = resolve
this.flushStdIn();
});
},
async read(ptr, nbytes) {
this._nbytes = nbytes
this._readPointer = ptr
return new Promise((resolve, reject) => {
this._resolveRead = resolve
this.flushStdIn();
});
},
flushStdIn() {
if(this._resolveGetLine) {
var lineBreak = this.bufferStdIn.indexOf("\n")
if (lineBreak != -1) {
this._resolveGetLine(stringToNewUTF8(this.bufferStdIn.substring(0,lineBreak+1)))
this.bufferStdIn = this.bufferStdIn.substring(lineBreak+1)
this._resolveGetLine = null
}
}
if(this._resolveRead) {
// console.log(`read: ${this.bufferStdIn}`)
stringToUTF8(this.bufferStdIn, this._readPointer, this._nbytes);
this._resolveRead()
this.bufferStdIn = ""
this._resolveRead = null
}
},
putLine(data) {
console.log("Client: ",data)
const str = data + '\r\n'
const byteLength = lengthBytesUTF8(str)
this.bufferStdIn += `Content-Length: ${byteLength}\r\n\r\n` + str
this.flushStdIn();
}
}
var input = ""
var i = 0;
function submit (ev) {
ev.preventDefault()
return false;
}
var stdoutBuffer = ""
var stderrBuffer = ""
var messageBuffer = []
var initialized = false;
var headerMode = true;
var header="";
var re = /Content-Length: (\d+)\r\n/i;
var contentLength = 0;
var content = []
var utf8decoder = new TextDecoder();
function flushMessageBuffer(){
if (initialized) {
while(messageBuffer.length > 0) {
@ -83,16 +24,29 @@ var Module = {
"arguments": ["--worker"],
"preRun": [function() {
function stdin() {
if (i < input.length) {
i++;
return input.charCodeAt(i);// Return ASCII code of character, or null if no input
} else {
return null
}
return null;
}
function stdout(asciiCode) {
stdoutBuffer += String.fromCharCode(asciiCode)
if (headerMode) {
header += String.fromCharCode(asciiCode)
if (header.endsWith('\r\n\r\n')) {
const found = header.match(re)
if (found == null) { console.error(`Invalid header: ${header}`) }
contentLength = parseInt(found[1])
content = []
headerMode = false
}
} else {
content.push(asciiCode)
if (content.length == contentLength) {
const message = utf8decoder.decode(new Uint8Array(content))
console.log(`Server: ${message}`)
postMessage(message);
headerMode = true
header = ''
}
}
}
function stderr(asciiCode) {
@ -114,19 +68,12 @@ importScripts("server.js")
onmessage = (ev) => {
console.log(`Client: ${ev.data}`)
messageBuffer.push(ev.data);
flushMessageBuffer();
}
IO.listenPutStr(message => {
postMessage(message)
})
setInterval(() => {
if (stdoutBuffer !== "") {
console.log(stdoutBuffer);
stdoutBuffer = ""
}
if (stderrBuffer !== "") {
console.error(stderrBuffer);
stderrBuffer = ""

@ -0,0 +1,87 @@
import Lean.Server.Watchdog
import GameServer.Game
namespace WasmServer.Watchdog
open Lean
open Server
open Watchdog
open IO
open Lsp
open JsonRpc
open System.Uri
def counter := IO.mkRef 0
def readLspRequestAs (s : String) (expectedMethod : String) (α : Type) [FromJson α] : IO (Request α) := do
let j ← ofExcept (Json.parse s)
let m ← match fromJson? j with
| Except.ok (m : JsonRpc.Message) => pure m
| Except.error inner => throw $ userError s!"JSON '{j.compress}' did not have the format of a JSON-RPC message.\n{inner}"
let initRequest ← match m with
| Message.request id method params? =>
if method = expectedMethod then
let j := toJson params?
match fromJson? j with
| Except.ok v => pure $ JsonRpc.Request.mk id expectedMethod (v : α)
| Except.error inner => throw $ userError s!"Unexpected param '{j.compress}' for method '{expectedMethod}'\n{inner}"
else
throw $ userError s!"Expected method '{expectedMethod}', got method '{method}'"
| _ => throw $ userError s!"Expected JSON-RPC request, got: '{(toJson m).compress}'"
@[export game_send_message]
def sendMessage (s : String) : IO Unit := do
-- IO.println s!"received {s}"
-- if args.length < 2 then
-- throwServerError s!"Expected 1-3 command line arguments in addition to `--server`:
-- game directory, the name of the main module (optional), and the name of the game (optional)."
-- let gameDir := args[1]!
-- let module := if args.length < 3 then defaultGameModule else args[2]!
-- let gameName := if args.length < 4 then defaultGameName else args[3]!
-- let workerPath := "./gameserver"
-- -- TODO: Do the following commands slow us down?
-- let srcSearchPath ← initSrcSearchPath (← getBuildDir)
-- let references ← IO.mkRef (← loadReferences)
-- let fileWorkersRef ← IO.mkRef (RBMap.empty : FileWorkerMap)
-- -- let i ← maybeTee "wdIn.txt" false i
-- -- let o ← maybeTee "wdOut.txt" true o
-- -- let e ← maybeTee "wdErr.txt" true e
-- let state : GameServerState := {
-- env := ← importModules #[] {} 0 --← createEnv gameDir module,
-- game := "TEST",
-- gameDir := "test",
-- inventory := #[]
-- difficulty := 0
-- }
let initRequest ← readLspRequestAs s "initialize" InitializeParams
-- We misuse the `rootUri` field to the gameName
let rootUri? := "TEST"
let initRequest := {initRequest with param := {initRequest.param with rootUri?}}
let o ← IO.getStdout
o.writeLspResponse {
id := initRequest.id
result := {
capabilities := mkLeanServerCapabilities
serverInfo? := some {
name := "Lean 4 Game Server"
version? := "0.1.1"
}
: InitializeResult
}
}
-- let context : ServerContext := {
-- hIn := i
-- hOut := o
-- hLog := e
-- args := args
-- fileWorkersRef := fileWorkersRef
-- initParams := initRequest.param
-- workerPath
-- srcSearchPath
-- references
-- }
-- discard $ ReaderT.run (StateT.run initAndRunWatchdogAux state) context
return ()

@ -1,5 +1,6 @@
import GameServer.FileWorker
import GameServer.Watchdog
import GameServer.WasmServer
import GameServer.Commands
-- TODO: The only reason we import `Commands` is so that it gets built to on `lake build`

@ -1,30 +0,0 @@
import Lean.Server.Watchdog
open Lean
open Server
open Watchdog
open Lsp
open IO
open JsonRpc
#check JsonRpc.Request
@[export game_send_message]
def sendMessage (s : String) : IO Unit := do
let expectedMethod := "initialize"
let j ← ofExcept (Json.parse s)
let m ← match fromJson? j with
| Except.ok (m : JsonRpc.Message) => pure m
| Except.error inner => throw $ userError s!"JSON '{j.compress}' did not have the format of a JSON-RPC message.\n{inner}"
let initRequest ← match m with
| Message.request id method params? =>
if method = expectedMethod then
let j := toJson params?
match fromJson? j with
| Except.ok v => pure $ JsonRpc.Request.mk id expectedMethod (v : InitializeParams)
| Except.error inner => throw $ userError s!"Unexpected param '{j.compress}' for method '{expectedMethod}'\n{inner}"
else
throw $ userError s!"Expected method '{expectedMethod}', got method '{method}'"
| _ => throw $ userError s!"Expected JSON-RPC request, got: '{(toJson m).compress}'"
IO.println s!"{initRequest.param.editDelay}"
return ()

@ -10,8 +10,3 @@ lean_exe gameserver {
root := `Main
supportInterpreter := true
}
@[default_target]
lean_lib WasmServer where
defaultFacets := #[LeanLib.staticFacet]

@ -7,7 +7,7 @@ extern lean_object* game_send_message(lean_object*, lean_object*);
extern void lean_initialize_runtime_module();
extern void lean_initialize();
extern void lean_io_mark_end_initialization();
extern lean_object * initialize_WasmServer(uint8_t builtin, lean_object *);
extern lean_object * initialize_GameServer_WasmServer(uint8_t builtin, lean_object *);
int main() {
lean_initialize();
@ -16,7 +16,7 @@ int main() {
// use same default as for Lean executables
uint8_t builtin = 1;
lean_object * io_world = lean_io_mk_world();
res = initialize_WasmServer(builtin, io_world);
res = initialize_GameServer_WasmServer(builtin, io_world);
if (lean_io_result_is_ok(res)) {
lean_dec_ref(res);
} else {

@ -10,5 +10,5 @@ OUT_DIR=../client/public
LEAN_SYSROOT=/home/alex/Projects/lean4/build/release/stage1
LEAN_LIBDIR=$LEAN_SYSROOT/lib/lean
emcc -o $OUT_DIR/server.js main.c -I $LEAN_SYSROOT/include -L $LEAN_LIBDIR build/ir/WasmServer.c -lInit -lLean -lleancpp -lleanrt \
emcc -o $OUT_DIR/server.js main.c -I $LEAN_SYSROOT/include -L $LEAN_LIBDIR build/ir/GameServer/*.c -lInit -lLean -lleancpp -lleanrt \
-sFORCE_FILESYSTEM -lnodefs.js -s EXIT_RUNTIME=0 -s MAIN_MODULE=1 -s LINKABLE=1 -s EXPORT_ALL=1 -s ALLOW_MEMORY_GROWTH=1 -fwasm-exceptions -pthread -flto

Loading…
Cancel
Save