Skip to content

Commit 7d3006d

Browse files
ospencerphated
andauthored
feat!: Rework preopened directories (#1656)
* 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>
1 parent f93afef commit 7d3006d

File tree

11 files changed

+493
-70
lines changed

11 files changed

+493
-70
lines changed

cli/bin/grain.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ class GrainCommand extends commander.Command {
119119
num
120120
);
121121
cmd.forwardOption("--import-memory", "import the memory from `env.memory`");
122+
cmd.option("--dir <dir...>", "directory to preopen");
122123
cmd.forwardOption(
123124
"--compilation-mode <mode>",
124125
"compilation mode (advanced use only)"

cli/bin/run.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,19 @@ module.exports = async function run(filename, options) {
66
let basePath = path.dirname(filename);
77
let includeDirs = [basePath, ...options.includeDirs, options.stdlib];
88
let locator = runner.defaultFileLocator(includeDirs);
9+
10+
let preopens = {};
11+
options.dir?.forEach((preopen) => {
12+
let [guestDir, hostDir = guestDir] = preopen.split("=");
13+
preopens[guestDir] = hostDir;
14+
});
15+
916
let GrainRunner = runner.buildGrainRunner(locator, {
1017
initialMemoryPages: options.initialMemoryPages,
1118
maximumMemoryPages: options.maximumMemoryPages,
19+
preopenDirs: preopens,
1220
});
21+
1322
if (options.printOutput) {
1423
let result = await GrainRunner.runFileUnboxed(filename);
1524
await GrainRunner.ensureStringModule();

compiler/test/TestFramework.re

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ let () =
2323

2424
let test_dir = Fp.At.(Filepath.get_cwd() / "test");
2525
let test_libs_dir = Fp.At.(test_dir / "test-libs");
26+
let test_data_dir = Fp.At.(test_dir / "test-data");
2627
let test_input_dir = Fp.At.(test_dir / "input");
2728
let test_output_dir = Fp.At.(test_dir / "output");
2829
let test_stdlib_dir = Fp.At.(test_dir / "stdlib");

compiler/test/runner.re

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,18 @@ let run = (~num_pages=?, file) => {
166166

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

169+
let preopen =
170+
Printf.sprintf(
171+
"--dir=%s=%s",
172+
"/test/test-data",
173+
Filepath.to_string(test_data_dir),
174+
);
175+
169176
let cmd =
170177
Array.concat([
171178
[|"grain", "run"|],
172179
mem_flags,
173-
[|"-S", stdlib, "-I", Filepath.to_string(test_libs_dir)|],
180+
[|"-S", stdlib, "-I", Filepath.to_string(test_libs_dir), preopen|],
174181
[|file|],
175182
]);
176183

compiler/test/stdlib/sys.file.test.gr

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,44 @@ include "result"
66

77
// fdRead
88
let foo = Result.unwrap(
9-
Fs.pathOpen(
10-
Fs.pwdfd,
11-
[Fs.SymlinkFollow],
12-
"test/test-data/foo.txt",
9+
Fs.open("test/test-data/foo.txt", [Fs.Create], [Fs.FdRead], [Fs.FdRead], [])
10+
)
11+
12+
let (buf, nread) = Result.unwrap(Fs.fdRead(foo, 40))
13+
14+
Fs.fdClose(foo)
15+
16+
assert buf == Bytes.fromString("foo, bar, & baz")
17+
assert nread == 15
18+
19+
// Check absolute path
20+
let foo = Result.unwrap(
21+
Fs.open("/test/test-data/foo.txt", [Fs.Create], [Fs.FdRead], [Fs.FdRead], [])
22+
)
23+
24+
let (buf, nread) = Result.unwrap(Fs.fdRead(foo, 40))
25+
26+
Fs.fdClose(foo)
27+
28+
assert buf == Bytes.fromString("foo, bar, & baz")
29+
assert nread == 15
30+
31+
// Check relative to current directory
32+
let foo = Result.unwrap(
33+
Fs.open("./test/test-data/foo.txt", [Fs.Create], [Fs.FdRead], [Fs.FdRead], [])
34+
)
35+
36+
let (buf, nread) = Result.unwrap(Fs.fdRead(foo, 40))
37+
38+
Fs.fdClose(foo)
39+
40+
assert buf == Bytes.fromString("foo, bar, & baz")
41+
assert nread == 15
42+
43+
// Check path resolution
44+
let foo = Result.unwrap(
45+
Fs.open(
46+
"/test/test-data/../test-data/foo.txt",
1347
[Fs.Create],
1448
[Fs.FdRead],
1549
[Fs.FdRead],
@@ -26,9 +60,7 @@ assert nread == 15
2660

2761
// fdWrite
2862
let foo = Result.unwrap(
29-
Fs.pathOpen(
30-
Fs.pwdfd,
31-
[Fs.SymlinkFollow],
63+
Fs.open(
3264
"test/test-data/bar.txt",
3365
[Fs.Create, Fs.Truncate],
3466
[Fs.FdRead, Fs.FdWrite, Fs.FdSeek, Fs.FdSetSize],

js-runner/src/core/grain-module.js

Lines changed: 2 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,5 @@
1-
import { WASI } from "@wasmer/wasi/lib/index.cjs";
2-
import { WasmFs } from "@wasmer/wasmfs";
31
import wasmmap from "wasm-sourcemap";
42

5-
let bindings;
6-
7-
if (__RUNNER_BROWSER) {
8-
const wasmFs = new WasmFs();
9-
const decoder = new TextDecoder("utf-8");
10-
// Monkeypatching the writeSync for stdout/stderr printing
11-
const originalWriteSync = wasmFs.fs.writeSync;
12-
wasmFs.fs.writeSync = (fd, buf, offset, length, position) => {
13-
if (fd === 1) {
14-
console.log(decoder.decode(buf));
15-
return;
16-
}
17-
if (fd === 2) {
18-
console.error(decoder.decode(buf));
19-
return;
20-
}
21-
22-
originalWriteSync(fd, buf, offset, length, position);
23-
};
24-
bindings = {
25-
...wasiBindings.default,
26-
fs: wasmFs.fs,
27-
};
28-
} else {
29-
bindings = wasiBindings.default;
30-
}
31-
32-
export const wasi = new WASI({
33-
args: __RUNNER_BROWSER ? [] : process.argv,
34-
env: __RUNNER_BROWSER ? {} : process.env,
35-
bindings,
36-
preopens: {
37-
"/sandbox": __RUNNER_BROWSER ? "" : process.cwd(),
38-
},
39-
});
40-
413
export class GrainModule {
424
constructor(wasmModule, name) {
435
this.wasmModule = wasmModule;
@@ -124,11 +86,11 @@ export class GrainModule {
12486
return this.requiredExport("_gtype_metadata")();
12587
}
12688

127-
start() {
89+
start(wasi) {
12890
wasi.start(this.instantiated);
12991
}
13092

131-
runUnboxed() {
93+
runUnboxed(wasi) {
13294
// Only the tests currently rely on this.
13395
wasi.setMemory(this.requiredExport("memory"));
13496
return this.requiredExport("_gmain")();

js-runner/src/core/runner.js

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,35 @@
1-
import { wasi, readFile, readURL, readBuffer } from "./grain-module";
1+
import { WASI } from "@wasmer/wasi/lib/index.cjs";
2+
import { WasmFs } from "@wasmer/wasmfs";
3+
import { readFile, readURL, readBuffer } from "./grain-module";
24
import { GRAIN_STRING_HEAP_TAG, GRAIN_GENERIC_HEAP_TAG_TYPE } from "./tags";
35

6+
let bindings;
7+
8+
if (__RUNNER_BROWSER) {
9+
const wasmFs = new WasmFs();
10+
const decoder = new TextDecoder("utf-8");
11+
// Monkeypatching the writeSync for stdout/stderr printing
12+
const originalWriteSync = wasmFs.fs.writeSync;
13+
wasmFs.fs.writeSync = (fd, buf, offset, length, position) => {
14+
if (fd === 1) {
15+
console.log(decoder.decode(buf));
16+
return;
17+
}
18+
if (fd === 2) {
19+
console.error(decoder.decode(buf));
20+
return;
21+
}
22+
23+
originalWriteSync(fd, buf, offset, length, position);
24+
};
25+
bindings = {
26+
...wasiBindings.default,
27+
fs: wasmFs.fs,
28+
};
29+
} else {
30+
bindings = wasiBindings.default;
31+
}
32+
433
const MALLOC_MODULE = "GRAIN$MODULE$runtime/gc";
534
const STRING_MODULE = "GRAIN$MODULE$runtime/string";
635

@@ -24,6 +53,12 @@ export class GrainRunner {
2453
element: "anyfunc",
2554
initial: 1024,
2655
});
56+
this.wasi = new WASI({
57+
args: __RUNNER_BROWSER ? [] : process.argv,
58+
env: __RUNNER_BROWSER ? {} : process.env,
59+
bindings,
60+
preopens: opts.preopenDirs,
61+
});
2762
}
2863

2964
get memoryManager() {
@@ -52,7 +87,7 @@ export class GrainRunner {
5287
throw new Error(`Failed to ensure string module.`);
5388
}
5489
await this.load(STRING_MODULE, located);
55-
located.start();
90+
located.start(this.wasi);
5691
}
5792

5893
grainValueToString(v) {
@@ -117,7 +152,7 @@ export class GrainRunner {
117152
continue;
118153
}
119154
if (imp.module.startsWith("wasi_")) {
120-
Object.assign(this.imports, wasi.getImports(mod.wasmModule));
155+
Object.assign(this.imports, this.wasi.getImports(mod.wasmModule));
121156
continue;
122157
}
123158
// Should return an instance of GrainModule
@@ -135,7 +170,7 @@ export class GrainRunner {
135170
located.loadTypeMetadata();
136171
}
137172
if (located.isStartable) {
138-
located.start();
173+
located.start(this.wasi);
139174
}
140175
this.ptrZero = this.ptr;
141176
this.imports[imp.module] = located.exports;
@@ -166,12 +201,12 @@ export class GrainRunner {
166201

167202
async runFileUnboxed(path) {
168203
let module = await this.loadFile(path);
169-
return module.runUnboxed();
204+
return module.runUnboxed(this.wasi);
170205
}
171206

172207
async runFile(path) {
173208
let module = await this.loadFile(path);
174-
return module.start();
209+
return module.start(this.wasi);
175210
}
176211

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

182217
async runURL(path) {
183218
let module = await this.loadURL(path);
184-
return module.start();
219+
return module.start(this.wasi);
185220
}
186221

187222
async runURLUnboxed(path) {
188223
let module = await this.loadURL(path);
189-
return module.runUnboxed();
224+
return module.runUnboxed(this.wasi);
190225
}
191226

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

197232
async runBuffer(buffer) {
198233
let module = await this.loadBuffer(buffer);
199-
return module.start();
234+
return module.start(this.wasi);
200235
}
201236

202237
async runBufferUnboxed(buffer) {
203238
let module = await this.loadBuffer(buffer);
204-
return module.runUnboxed();
239+
return module.runUnboxed(this.wasi);
205240
}
206241
}

stdlib/runtime/wasi.gr

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ provide foreign wasm fd_pread: (
6565
WasmI64,
6666
WasmI32,
6767
) -> WasmI32 from "wasi_snapshot_preview1"
68+
provide foreign wasm fd_prestat_get: (
69+
WasmI32,
70+
WasmI32,
71+
) -> WasmI32 from "wasi_snapshot_preview1"
72+
provide foreign wasm fd_prestat_dir_name: (
73+
WasmI32,
74+
WasmI32,
75+
WasmI32,
76+
) -> WasmI32 from "wasi_snapshot_preview1"
6877
/**
6978
* Invokes the `fd_write` system call.
7079
*

stdlib/runtime/wasi.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@ fd_read : (WasmI32, WasmI32, WasmI32, WasmI32) -> WasmI32
8080
fd_pread : (WasmI32, WasmI32, WasmI32, WasmI64, WasmI32) -> WasmI32
8181
```
8282

83+
### Wasi.**fd_prestat_get**
84+
85+
```grain
86+
fd_prestat_get : (WasmI32, WasmI32) -> WasmI32
87+
```
88+
89+
### Wasi.**fd_prestat_dir_name**
90+
91+
```grain
92+
fd_prestat_dir_name : (WasmI32, WasmI32, WasmI32) -> WasmI32
93+
```
94+
8395
### Wasi.**fd_write**
8496

8597
```grain

0 commit comments

Comments
 (0)