package main import ( "bytes" "context" "fmt" "os" "os/exec" "strings" "time" ) const ( dataRootDir = "/app/data" // Stores *.swift files. dataSwiftRootDir = dataRootDir + "/swift" // Stores *.wasm files. dataWasmRootDir = dataRootDir + "/wasm" // Stores *.cwasm files (compiled wasm generated by "wasmtime compile"). dataCwasmRootDir = dataRootDir + "/cwasm" wasmMaxMemorySize = 10 * 1024 * 1024 // 10 MiB ) func prepareDirectories() error { if err := os.MkdirAll(dataSwiftRootDir, 0755); err != nil { return err } if err := os.MkdirAll(dataWasmRootDir, 0755); err != nil { return err } if err := os.MkdirAll(dataCwasmRootDir, 0755); err != nil { return err } return nil } func calcFilePath(hash, ext string) string { return fmt.Sprintf("%s/%s/%s.%s", dataRootDir, ext, hash, ext) } func execCommandWithTimeout( ctx context.Context, maxDuration time.Duration, makeCmd func(context.Context) *exec.Cmd, ) (string, string, error) { ctx, cancel := context.WithTimeout(ctx, maxDuration) defer cancel() cmd := makeCmd(ctx) var stdout bytes.Buffer var stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr exitCh := make(chan error) go func() { exitCh <- cmd.Run() }() select { case <-ctx.Done(): return stdout.String(), stderr.String(), ctx.Err() case err := <-exitCh: return stdout.String(), stderr.String(), err } } func convertCommandErrorToResultType(err error, isCompile bool) string { if err != nil { if err == context.DeadlineExceeded { return resultTimeout } if isCompile { return resultCompileError } return resultRuntimeError } return resultSuccess } func execSwiftCompile( ctx context.Context, code string, codeHash string, maxDuration time.Duration, ) swiftCompileResponseData { inPath := calcFilePath(codeHash, "swift") outPath := calcFilePath(codeHash, "wasm") if err := os.WriteFile(inPath, []byte(code), 0644); err != nil { return swiftCompileResponseData{ Status: resultInternalError, Stdout: "", Stderr: err.Error(), } } stdout, stderr, err := execCommandWithTimeout( ctx, maxDuration, func(ctx context.Context) *exec.Cmd { return exec.CommandContext( ctx, "swiftc", "-target", "wasm32-unknown-wasi", "-o", outPath, inPath, ) }, ) return swiftCompileResponseData{ Status: convertCommandErrorToResultType(err, true), Stdout: stdout, Stderr: stderr, } } func execWasmCompile( ctx context.Context, codeHash string, maxDuration time.Duration, ) wasmCompileResponseData { inPath := calcFilePath(codeHash, "wasm") outPath := calcFilePath(codeHash, "cwasm") stdout, stderr, err := execCommandWithTimeout( ctx, maxDuration, func(ctx context.Context) *exec.Cmd { return exec.CommandContext( ctx, "wasmtime", "compile", "-O", "opt-level=0", "-C", "cache=n", "-W", fmt.Sprintf("max-memory-size=%d", wasmMaxMemorySize), "-o", outPath, inPath, ) }, ) return wasmCompileResponseData{ Status: convertCommandErrorToResultType(err, true), Stdout: stdout, Stderr: stderr, } } func execTestRun( ctx context.Context, codeHash string, stdin string, maxDuration time.Duration, ) testRunResponseData { inPath := calcFilePath(codeHash, "cwasm") stdout, stderr, err := execCommandWithTimeout( ctx, maxDuration, func(ctx context.Context) *exec.Cmd { cmd := exec.CommandContext( ctx, "wasmtime", "run", "--allow-precompiled", inPath, ) cmd.Stdin = strings.NewReader(stdin) return cmd }, ) return testRunResponseData{ Status: convertCommandErrorToResultType(err, false), Stdout: stdout, Stderr: stderr, } }