Skip to content

Commit 0237c62

Browse files
committed
Add compile and run functionality with button integration and context updates
1 parent 9f4baa0 commit 0237c62

11 files changed

+281
-11
lines changed

lib/src/sysroot.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@ const toMap = (arr: Array<[string, Inode]>) => {
4444
};
4545

4646
export const load_default_sysroot = async (): Promise<PreopenDirectory> => {
47-
const sysroot_part = await load_sysroot_part('wasm32-wasip1-threads');
47+
const sysroot_part = await load_sysroot_part('wasm32-wasip1');
4848
const sysroot = new PreopenDirectory(
4949
"/sysroot",
5050
toMap([
5151
["lib", new Directory([
5252
["rustlib", new Directory([
53-
["wasm32-wasip1-threads", new Directory([
53+
["wasm32-wasip1", new Directory([
5454
["lib", sysroot_part]
55-
])]
55+
])],
5656
])]
5757
])]
5858
])

page/src/App.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { SetupMyTerminal } from "./xterm";
33
import type { WASIFarmRef } from "@oligami/browser_wasi_shim-threads";
44
import type { Ctx } from "./ctx";
55
import { MonacoEditor } from "solid-monaco";
6-
import { default_value } from "./config";
6+
import { default_value, rust_file } from "./config";
7+
import { Button } from "./btn";
78

89
const App = (props: {
910
ctx: Ctx;
@@ -13,7 +14,8 @@ const App = (props: {
1314
// Use monaco and editor instances here
1415
};
1516
const handleEditorChange = (value) => {
16-
// Handle editor change
17+
// Handle editor value change
18+
rust_file.data = new TextEncoder().encode(value);
1719
};
1820

1921
return (
@@ -23,9 +25,11 @@ const App = (props: {
2325
value={default_value}
2426
height="30vh"
2527
onMount={handleMount}
28+
onChange={handleEditorChange}
2629
/>
2730
{/* <p class="text-4xl text-green-700 text-center py-20">Hello tailwind!</p> */}
2831
<SetupMyTerminal ctx={props.ctx} callback={props.callback} />
32+
<Button />
2933
</>
3034
);
3135
};

page/src/btn.tsx

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { compile_and_run } from "./compile_and_run";
2+
3+
export const Button = () => {
4+
return (
5+
<button
6+
type="button"
7+
onClick={() => {
8+
console.log("button clicked");
9+
compile_and_run();
10+
}}
11+
class="text-2xl text-green-700 text-center py-20"
12+
>
13+
Compile and Run
14+
</button>
15+
);
16+
};

page/src/cat.ts

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import type { WASIFarmAnimal } from "@oligami/browser_wasi_shim-threads";
2+
import { wasi } from "@bjorn3/browser_wasi_shim";
3+
4+
export const get_data = (path__: string, _animal: WASIFarmAnimal): Uint8Array => {
5+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
6+
const animal = _animal as any;
7+
8+
// path is absolute
9+
let path = path__;
10+
if (!path.startsWith("/")) {
11+
path = `/${path}`;
12+
}
13+
14+
// first: get opened fd dir name
15+
let root_fd: number;
16+
const dir_names: Map<number, string> = new Map();
17+
for (let fd = 0; fd < animal.fd_map.length; fd++) {
18+
const [mapped_fd, wasi_farm_ref] = animal.get_fd_and_wasi_ref(fd);
19+
if (mapped_fd === undefined || wasi_farm_ref === undefined) {
20+
continue;
21+
}
22+
const [prestat, ret] = wasi_farm_ref.fd_prestat_get(mapped_fd);
23+
if (ret !== wasi.ERRNO_SUCCESS) {
24+
continue;
25+
}
26+
if (prestat) {
27+
const [tag, name_len] = prestat;
28+
if (tag === wasi.PREOPENTYPE_DIR) {
29+
const [path, ret] = wasi_farm_ref.fd_prestat_dir_name(
30+
mapped_fd,
31+
name_len,
32+
);
33+
if (ret !== wasi.ERRNO_SUCCESS) {
34+
continue;
35+
}
36+
if (path) {
37+
const decoded_path = new TextDecoder().decode(path);
38+
dir_names.set(fd, decoded_path);
39+
if (decoded_path === "/") {
40+
root_fd = fd;
41+
}
42+
}
43+
}
44+
}
45+
}
46+
47+
// second: most match path
48+
let matched_fd = root_fd;
49+
let matched_dir_len = 1;
50+
const parts_path = path.split("/");
51+
for (const [fd, dir_name] of dir_names) {
52+
const parts_dir_name = dir_name.split("/");
53+
let dir_len = 0;
54+
for (let i = 0; i < parts_dir_name.length; i++) {
55+
if (parts_dir_name[i] === parts_path[i]) {
56+
dir_len++;
57+
} else {
58+
break;
59+
}
60+
}
61+
if (dir_len > matched_dir_len) {
62+
matched_fd = fd;
63+
matched_dir_len = dir_len;
64+
}
65+
}
66+
67+
if (matched_dir_len === 0) {
68+
throw new Error("no matched dir");
69+
}
70+
71+
console.log("matched_dir_name", dir_names.get(matched_fd));
72+
73+
// third: tale the rest of path
74+
const rest_path = parts_path.slice(matched_dir_len).join("/");
75+
console.log("rest_path", rest_path);
76+
77+
// fourth: open file
78+
const [mapped_fd, wasi_farm_ref_n] = animal.get_fd_and_wasi_ref_n(matched_fd);
79+
const [opened_fd, ret] = animal.wasi_farm_refs[wasi_farm_ref_n].path_open(
80+
mapped_fd,
81+
0,
82+
new TextEncoder().encode(rest_path),
83+
0,
84+
0n,
85+
0n,
86+
0,
87+
);
88+
89+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
90+
const mapped_opened_fd = animal.map_new_fd_and_notify(
91+
opened_fd,
92+
wasi_farm_ref_n,
93+
);
94+
95+
if (ret !== wasi.ERRNO_SUCCESS) {
96+
throw new Error("failed to open file");
97+
}
98+
99+
// fifth: read file
100+
let file_data: Uint8Array = new Uint8Array();
101+
let offset = 0n;
102+
103+
// eslint-disable-next-line no-constant-condition
104+
while (true) {
105+
console.log("offset", offset);
106+
107+
const iovs = new Uint32Array(2);
108+
// buf_ptr so any value
109+
iovs[0] = 0;
110+
// buf_len
111+
iovs[1] = 1024;
112+
const [nread_and_buf, ret] = animal.wasi_farm_refs[
113+
wasi_farm_ref_n
114+
].fd_pread(opened_fd, iovs, offset);
115+
if (ret !== wasi.ERRNO_SUCCESS) {
116+
throw new Error("failed to read file");
117+
}
118+
if (!nread_and_buf) {
119+
throw new Error("failed to read file");
120+
}
121+
const [nread, buf] = nread_and_buf;
122+
if (nread === 0) {
123+
break;
124+
}
125+
const new_data = new Uint8Array(file_data.length + nread);
126+
new_data.set(file_data);
127+
new_data.set(buf, file_data.length);
128+
file_data = new_data;
129+
offset += BigInt(nread);
130+
if (nread < 1024) {
131+
break;
132+
}
133+
}
134+
135+
animal.wasi_farm_refs[wasi_farm_ref_n].fd_close(opened_fd);
136+
137+
return file_data;
138+
}

page/src/cmd_parser.ts

+22
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import { SharedObject, SharedObjectRef } from "@oligami/shared-object";
22
import type { Ctx } from "./ctx";
3+
import { get_data } from "./cat";
34

45
let waiter: SharedObject;
56
let is_all_done = false;
7+
let is_cmd_run_end = true;
8+
let end_of_exec = false;
69

710
export const parser_setup = async (ctx: Ctx) => {
811
const n = 1;
@@ -20,6 +23,14 @@ export const parser_setup = async (ctx: Ctx) => {
2023
is_all_done: (): boolean => {
2124
return is_all_done;
2225
},
26+
is_cmd_run_end: () => {
27+
return is_cmd_run_end;
28+
},
29+
set_end_of_exec: (
30+
_end_of_exec: boolean,
31+
) => {
32+
end_of_exec = _end_of_exec;
33+
}
2334
},
2435
ctx.waiter_id,
2536
);
@@ -40,8 +51,10 @@ const all_done = async (ctx: Ctx) => {
4051
>();
4152
const ls = new SharedObjectRef(ctx.ls_id).proxy<(...string) => void>();
4253
const tree = new SharedObjectRef(ctx.tree_id).proxy<(...string) => void>();
54+
const exec_file = new SharedObjectRef(ctx.exec_file_id).proxy<(...string) => void>();
4355

4456
cmd_parser = new SharedObject((...args) => {
57+
is_cmd_run_end = false;
4558
(async (args: string[]) => {
4659
console.log(args);
4760

@@ -64,10 +77,19 @@ const all_done = async (ctx: Ctx) => {
6477
console.log("tree");
6578
await terminal("executing tree...\r\n");
6679
await tree(...args.slice(1));
80+
} else if (cmd.includes("/")) {
81+
console.log("cmd includes /");
82+
await terminal("executing file...\r\n");
83+
end_of_exec = false;
84+
await exec_file(...args);
85+
while (!end_of_exec) {
86+
await new Promise<void>((resolve) => setTimeout(resolve, 100));
87+
}
6788
} else {
6889
await terminal(`command not found: ${cmd}\r\n`);
6990
}
7091
await terminal(">");
92+
is_cmd_run_end = true;
7193
})(args);
7294
}, ctx.cmd_parser_id);
7395

page/src/compile_and_run.ts

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { SharedObjectRef } from "@oligami/shared-object";
2+
import type { Ctx } from "./ctx";
3+
4+
let ctx: Ctx;
5+
let cmd_parser: (...string) => void;
6+
let waiter: {
7+
is_all_done: () => boolean;
8+
is_cmd_run_end: () => boolean;
9+
};
10+
let terminal: (string) => void;
11+
12+
export const compile_and_run_setup = (_ctx: Ctx) => {
13+
ctx = _ctx;
14+
15+
waiter = new SharedObjectRef(ctx.waiter_id).proxy<{
16+
is_all_done: () => boolean;
17+
is_cmd_run_end: () => boolean;
18+
}>();
19+
}
20+
21+
let can_setup = false;
22+
23+
export const compile_and_run = async () => {
24+
if (!can_setup) {
25+
if (await waiter.is_all_done()) {
26+
terminal = new SharedObjectRef(ctx.terminal_id).proxy<(string) => void>();
27+
28+
cmd_parser = new SharedObjectRef(ctx.cmd_parser_id).proxy<
29+
(...string) => void
30+
>();
31+
can_setup = true;
32+
} else {
33+
terminal = new SharedObjectRef(ctx.terminal_id).proxy<(string) => void>();
34+
35+
await terminal("this is not done yet\r\n");
36+
}
37+
}
38+
39+
if (can_setup) {
40+
const exec = ["rustc", "/main.rs", "--sysroot", "/sysroot", "--target", "wasm32-wasip1", "--out-dir", "/tmp", "-Ccodegen-units=1"];
41+
await terminal(`${exec.join(" ")}\r\n`);
42+
await cmd_parser(...exec);
43+
while (!await waiter.is_cmd_run_end()) {
44+
await new Promise((resolve) => setTimeout(resolve, 100));
45+
}
46+
await terminal("/tmp/main.wasm\r\n");
47+
await cmd_parser("/tmp/main.wasm");
48+
while (!await waiter.is_cmd_run_end()) {
49+
await new Promise((resolve) => setTimeout(resolve, 100));
50+
}
51+
}
52+
}

page/src/config.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
import { File } from '@bjorn3/browser_wasi_shim';
2+
13
export const default_value = `// /main.rs
24
fn main() {
35
println!("Hello, world!");
46
}
57
`;
8+
9+
export const rust_file: File = new File(
10+
new TextEncoder().encode(default_value),
11+
);

page/src/ctx.ts

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export type Ctx = {
55
cmd_parser_id: string;
66
tree_id: string;
77
ls_id: string;
8+
exec_file_id: string;
89
};
910

1011
const gen_id = () => Math.random().toString(36).substring(7);
@@ -17,5 +18,6 @@ export const gen_ctx = (): Ctx => {
1718
cmd_parser_id: gen_id(),
1819
tree_id: gen_id(),
1920
ls_id: gen_id(),
21+
exec_file_id: gen_id(),
2022
};
2123
};

page/src/index.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { SharedObject, SharedObjectRef } from "@oligami/shared-object";
88
import MainWorker from "./worker?worker";
99
import { parser_setup } from "./cmd_parser";
1010
import "./monaco_worker";
11+
import { compile_and_run_setup } from "./compile_and_run";
1112

1213
const root = document.getElementById("root");
1314

@@ -23,6 +24,7 @@ const ctx = gen_ctx();
2324
const worker = new MainWorker();
2425

2526
parser_setup(ctx);
27+
compile_and_run_setup(ctx);
2628

2729
// send message to worker
2830
worker.postMessage({ ctx });

0 commit comments

Comments
 (0)