Skip to content

Commit

Permalink
feat!: Rework preopened directories (grain-lang#1656)
Browse files Browse the repository at this point in the history
* feat!: Rework preopened directories

* Update stdlib/sys/file.gr

Co-authored-by: Blaine Bublitz <blaine.bublitz@gmail.com>

* remove failure case

* Add `@since` to open

---------

Co-authored-by: Blaine Bublitz <blaine.bublitz@gmail.com>
  • Loading branch information
ospencer and phated authored Mar 16, 2023
1 parent f93afef commit 7d3006d
Show file tree
Hide file tree
Showing 11 changed files with 493 additions and 70 deletions.
1 change: 1 addition & 0 deletions cli/bin/grain.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class GrainCommand extends commander.Command {
num
);
cmd.forwardOption("--import-memory", "import the memory from `env.memory`");
cmd.option("--dir <dir...>", "directory to preopen");
cmd.forwardOption(
"--compilation-mode <mode>",
"compilation mode (advanced use only)"
Expand Down
9 changes: 9 additions & 0 deletions cli/bin/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,19 @@ module.exports = async function run(filename, options) {
let basePath = path.dirname(filename);
let includeDirs = [basePath, ...options.includeDirs, options.stdlib];
let locator = runner.defaultFileLocator(includeDirs);

let preopens = {};
options.dir?.forEach((preopen) => {
let [guestDir, hostDir = guestDir] = preopen.split("=");
preopens[guestDir] = hostDir;
});

let GrainRunner = runner.buildGrainRunner(locator, {
initialMemoryPages: options.initialMemoryPages,
maximumMemoryPages: options.maximumMemoryPages,
preopenDirs: preopens,
});

if (options.printOutput) {
let result = await GrainRunner.runFileUnboxed(filename);
await GrainRunner.ensureStringModule();
Expand Down
1 change: 1 addition & 0 deletions compiler/test/TestFramework.re
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ let () =

let test_dir = Fp.At.(Filepath.get_cwd() / "test");
let test_libs_dir = Fp.At.(test_dir / "test-libs");
let test_data_dir = Fp.At.(test_dir / "test-data");
let test_input_dir = Fp.At.(test_dir / "input");
let test_output_dir = Fp.At.(test_dir / "output");
let test_stdlib_dir = Fp.At.(test_dir / "stdlib");
Expand Down
9 changes: 8 additions & 1 deletion compiler/test/runner.re
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,18 @@ let run = (~num_pages=?, file) => {

let stdlib = Option.get(Grain_utils.Config.stdlib_dir^);

let preopen =
Printf.sprintf(
"--dir=%s=%s",
"/test/test-data",
Filepath.to_string(test_data_dir),
);

let cmd =
Array.concat([
[|"grain", "run"|],
mem_flags,
[|"-S", stdlib, "-I", Filepath.to_string(test_libs_dir)|],
[|"-S", stdlib, "-I", Filepath.to_string(test_libs_dir), preopen|],
[|file|],
]);

Expand Down
46 changes: 39 additions & 7 deletions compiler/test/stdlib/sys.file.test.gr
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,44 @@ include "result"

// fdRead
let foo = Result.unwrap(
Fs.pathOpen(
Fs.pwdfd,
[Fs.SymlinkFollow],
"test/test-data/foo.txt",
Fs.open("test/test-data/foo.txt", [Fs.Create], [Fs.FdRead], [Fs.FdRead], [])
)

let (buf, nread) = Result.unwrap(Fs.fdRead(foo, 40))

Fs.fdClose(foo)

assert buf == Bytes.fromString("foo, bar, & baz")
assert nread == 15

// Check absolute path
let foo = Result.unwrap(
Fs.open("/test/test-data/foo.txt", [Fs.Create], [Fs.FdRead], [Fs.FdRead], [])
)

let (buf, nread) = Result.unwrap(Fs.fdRead(foo, 40))

Fs.fdClose(foo)

assert buf == Bytes.fromString("foo, bar, & baz")
assert nread == 15

// Check relative to current directory
let foo = Result.unwrap(
Fs.open("./test/test-data/foo.txt", [Fs.Create], [Fs.FdRead], [Fs.FdRead], [])
)

let (buf, nread) = Result.unwrap(Fs.fdRead(foo, 40))

Fs.fdClose(foo)

assert buf == Bytes.fromString("foo, bar, & baz")
assert nread == 15

// Check path resolution
let foo = Result.unwrap(
Fs.open(
"/test/test-data/../test-data/foo.txt",
[Fs.Create],
[Fs.FdRead],
[Fs.FdRead],
Expand All @@ -26,9 +60,7 @@ assert nread == 15

// fdWrite
let foo = Result.unwrap(
Fs.pathOpen(
Fs.pwdfd,
[Fs.SymlinkFollow],
Fs.open(
"test/test-data/bar.txt",
[Fs.Create, Fs.Truncate],
[Fs.FdRead, Fs.FdWrite, Fs.FdSeek, Fs.FdSetSize],
Expand Down
42 changes: 2 additions & 40 deletions js-runner/src/core/grain-module.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,5 @@
import { WASI } from "@wasmer/wasi/lib/index.cjs";
import { WasmFs } from "@wasmer/wasmfs";
import wasmmap from "wasm-sourcemap";

let bindings;

if (__RUNNER_BROWSER) {
const wasmFs = new WasmFs();
const decoder = new TextDecoder("utf-8");
// Monkeypatching the writeSync for stdout/stderr printing
const originalWriteSync = wasmFs.fs.writeSync;
wasmFs.fs.writeSync = (fd, buf, offset, length, position) => {
if (fd === 1) {
console.log(decoder.decode(buf));
return;
}
if (fd === 2) {
console.error(decoder.decode(buf));
return;
}

originalWriteSync(fd, buf, offset, length, position);
};
bindings = {
...wasiBindings.default,
fs: wasmFs.fs,
};
} else {
bindings = wasiBindings.default;
}

export const wasi = new WASI({
args: __RUNNER_BROWSER ? [] : process.argv,
env: __RUNNER_BROWSER ? {} : process.env,
bindings,
preopens: {
"/sandbox": __RUNNER_BROWSER ? "" : process.cwd(),
},
});

export class GrainModule {
constructor(wasmModule, name) {
this.wasmModule = wasmModule;
Expand Down Expand Up @@ -124,11 +86,11 @@ export class GrainModule {
return this.requiredExport("_gtype_metadata")();
}

start() {
start(wasi) {
wasi.start(this.instantiated);
}

runUnboxed() {
runUnboxed(wasi) {
// Only the tests currently rely on this.
wasi.setMemory(this.requiredExport("memory"));
return this.requiredExport("_gmain")();
Expand Down
55 changes: 45 additions & 10 deletions js-runner/src/core/runner.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,35 @@
import { wasi, readFile, readURL, readBuffer } from "./grain-module";
import { WASI } from "@wasmer/wasi/lib/index.cjs";
import { WasmFs } from "@wasmer/wasmfs";
import { readFile, readURL, readBuffer } from "./grain-module";
import { GRAIN_STRING_HEAP_TAG, GRAIN_GENERIC_HEAP_TAG_TYPE } from "./tags";

let bindings;

if (__RUNNER_BROWSER) {
const wasmFs = new WasmFs();
const decoder = new TextDecoder("utf-8");
// Monkeypatching the writeSync for stdout/stderr printing
const originalWriteSync = wasmFs.fs.writeSync;
wasmFs.fs.writeSync = (fd, buf, offset, length, position) => {
if (fd === 1) {
console.log(decoder.decode(buf));
return;
}
if (fd === 2) {
console.error(decoder.decode(buf));
return;
}

originalWriteSync(fd, buf, offset, length, position);
};
bindings = {
...wasiBindings.default,
fs: wasmFs.fs,
};
} else {
bindings = wasiBindings.default;
}

const MALLOC_MODULE = "GRAIN$MODULE$runtime/gc";
const STRING_MODULE = "GRAIN$MODULE$runtime/string";

Expand All @@ -24,6 +53,12 @@ export class GrainRunner {
element: "anyfunc",
initial: 1024,
});
this.wasi = new WASI({
args: __RUNNER_BROWSER ? [] : process.argv,
env: __RUNNER_BROWSER ? {} : process.env,
bindings,
preopens: opts.preopenDirs,
});
}

get memoryManager() {
Expand Down Expand Up @@ -52,7 +87,7 @@ export class GrainRunner {
throw new Error(`Failed to ensure string module.`);
}
await this.load(STRING_MODULE, located);
located.start();
located.start(this.wasi);
}

grainValueToString(v) {
Expand Down Expand Up @@ -117,7 +152,7 @@ export class GrainRunner {
continue;
}
if (imp.module.startsWith("wasi_")) {
Object.assign(this.imports, wasi.getImports(mod.wasmModule));
Object.assign(this.imports, this.wasi.getImports(mod.wasmModule));
continue;
}
// Should return an instance of GrainModule
Expand All @@ -135,7 +170,7 @@ export class GrainRunner {
located.loadTypeMetadata();
}
if (located.isStartable) {
located.start();
located.start(this.wasi);
}
this.ptrZero = this.ptr;
this.imports[imp.module] = located.exports;
Expand Down Expand Up @@ -166,12 +201,12 @@ export class GrainRunner {

async runFileUnboxed(path) {
let module = await this.loadFile(path);
return module.runUnboxed();
return module.runUnboxed(this.wasi);
}

async runFile(path) {
let module = await this.loadFile(path);
return module.start();
return module.start(this.wasi);
}

async loadURL(url) {
Expand All @@ -181,12 +216,12 @@ export class GrainRunner {

async runURL(path) {
let module = await this.loadURL(path);
return module.start();
return module.start(this.wasi);
}

async runURLUnboxed(path) {
let module = await this.loadURL(path);
return module.runUnboxed();
return module.runUnboxed(this.wasi);
}

async loadBuffer(buffer) {
Expand All @@ -196,11 +231,11 @@ export class GrainRunner {

async runBuffer(buffer) {
let module = await this.loadBuffer(buffer);
return module.start();
return module.start(this.wasi);
}

async runBufferUnboxed(buffer) {
let module = await this.loadBuffer(buffer);
return module.runUnboxed();
return module.runUnboxed(this.wasi);
}
}
9 changes: 9 additions & 0 deletions stdlib/runtime/wasi.gr
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ provide foreign wasm fd_pread: (
WasmI64,
WasmI32,
) -> WasmI32 from "wasi_snapshot_preview1"
provide foreign wasm fd_prestat_get: (
WasmI32,
WasmI32,
) -> WasmI32 from "wasi_snapshot_preview1"
provide foreign wasm fd_prestat_dir_name: (
WasmI32,
WasmI32,
WasmI32,
) -> WasmI32 from "wasi_snapshot_preview1"
/**
* Invokes the `fd_write` system call.
*
Expand Down
12 changes: 12 additions & 0 deletions stdlib/runtime/wasi.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,18 @@ fd_read : (WasmI32, WasmI32, WasmI32, WasmI32) -> WasmI32
fd_pread : (WasmI32, WasmI32, WasmI32, WasmI64, WasmI32) -> WasmI32
```

### Wasi.**fd_prestat_get**

```grain
fd_prestat_get : (WasmI32, WasmI32) -> WasmI32
```

### Wasi.**fd_prestat_dir_name**

```grain
fd_prestat_dir_name : (WasmI32, WasmI32, WasmI32) -> WasmI32
```

### Wasi.**fd_write**

```grain
Expand Down
Loading

0 comments on commit 7d3006d

Please sign in to comment.