@tishlang/tish-format 1.0.12 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +49 -0
- package/LICENSE +13 -0
- package/README.md +138 -0
- package/bin/tish-format +0 -0
- package/crates/js_to_tish/Cargo.toml +11 -0
- package/crates/js_to_tish/README.md +18 -0
- package/crates/js_to_tish/src/error.rs +55 -0
- package/crates/js_to_tish/src/lib.rs +11 -0
- package/crates/js_to_tish/src/span_util.rs +35 -0
- package/crates/js_to_tish/src/transform/expr.rs +610 -0
- package/crates/js_to_tish/src/transform/stmt.rs +503 -0
- package/crates/js_to_tish/src/transform.rs +60 -0
- package/crates/tish/Cargo.toml +54 -0
- package/crates/tish/src/cargo_native_registry.rs +32 -0
- package/crates/tish/src/cli_help.rs +565 -0
- package/crates/tish/src/main.rs +781 -0
- package/crates/tish/src/repl_completion.rs +200 -0
- package/crates/tish/tests/cargo_example_compile.rs +67 -0
- package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
- package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
- package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
- package/crates/tish/tests/integration_test.rs +1095 -0
- package/crates/tish/tests/run_optimize_stdout_parity.rs +50 -0
- package/crates/tish/tests/shortcircuit.rs +65 -0
- package/crates/tish_ast/Cargo.toml +9 -0
- package/crates/tish_ast/src/ast.rs +620 -0
- package/crates/tish_ast/src/lib.rs +5 -0
- package/crates/tish_build_utils/Cargo.toml +11 -0
- package/crates/tish_build_utils/src/lib.rs +577 -0
- package/crates/tish_builtins/Cargo.toml +20 -0
- package/crates/tish_builtins/src/array.rs +441 -0
- package/crates/tish_builtins/src/construct.rs +159 -0
- package/crates/tish_builtins/src/globals.rs +213 -0
- package/crates/tish_builtins/src/helpers.rs +35 -0
- package/crates/tish_builtins/src/lib.rs +16 -0
- package/crates/tish_builtins/src/math.rs +89 -0
- package/crates/tish_builtins/src/object.rs +36 -0
- package/crates/tish_builtins/src/string.rs +647 -0
- package/crates/tish_builtins/src/symbol.rs +83 -0
- package/crates/tish_bytecode/Cargo.toml +17 -0
- package/crates/tish_bytecode/src/chunk.rs +96 -0
- package/crates/tish_bytecode/src/compiler.rs +1760 -0
- package/crates/tish_bytecode/src/encoding.rs +100 -0
- package/crates/tish_bytecode/src/lib.rs +19 -0
- package/crates/tish_bytecode/src/opcode.rs +142 -0
- package/crates/tish_bytecode/src/peephole.rs +189 -0
- package/crates/tish_bytecode/src/serialize.rs +163 -0
- package/crates/tish_bytecode/tests/break_continue_bytecode.rs +44 -0
- package/crates/tish_bytecode/tests/constant_folding.rs +84 -0
- package/crates/tish_bytecode/tests/sort_optimization.rs +31 -0
- package/crates/tish_compile/Cargo.toml +26 -0
- package/crates/tish_compile/src/codegen.rs +5332 -0
- package/crates/tish_compile/src/infer.rs +292 -0
- package/crates/tish_compile/src/lib.rs +164 -0
- package/crates/tish_compile/src/resolve.rs +1388 -0
- package/crates/tish_compile/src/types.rs +501 -0
- package/crates/tish_compile_js/Cargo.toml +18 -0
- package/crates/tish_compile_js/examples/jsx_vdom_smoke.tish +8 -0
- package/crates/tish_compile_js/src/codegen.rs +871 -0
- package/crates/tish_compile_js/src/error.rs +20 -0
- package/crates/tish_compile_js/src/lib.rs +26 -0
- package/crates/tish_compile_js/src/tests_jsx.rs +350 -0
- package/crates/tish_compiler_wasm/Cargo.toml +21 -0
- package/crates/tish_compiler_wasm/src/lib.rs +57 -0
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +473 -0
- package/crates/tish_core/Cargo.toml +26 -0
- package/crates/tish_core/src/console_style.rs +160 -0
- package/crates/tish_core/src/json.rs +387 -0
- package/crates/tish_core/src/lib.rs +17 -0
- package/crates/tish_core/src/macros.rs +36 -0
- package/crates/tish_core/src/uri.rs +118 -0
- package/crates/tish_core/src/value.rs +696 -0
- package/crates/tish_core/src/vmref.rs +178 -0
- package/crates/tish_cranelift/Cargo.toml +19 -0
- package/crates/tish_cranelift/src/lib.rs +43 -0
- package/crates/tish_cranelift/src/link.rs +117 -0
- package/crates/tish_cranelift/src/lower.rs +85 -0
- package/crates/tish_cranelift_runtime/Cargo.toml +25 -0
- package/crates/tish_cranelift_runtime/src/lib.rs +45 -0
- package/crates/tish_eval/Cargo.toml +45 -0
- package/crates/tish_eval/src/eval.rs +3717 -0
- package/crates/tish_eval/src/http.rs +188 -0
- package/crates/tish_eval/src/lib.rs +99 -0
- package/crates/tish_eval/src/natives.rs +399 -0
- package/crates/tish_eval/src/promise.rs +179 -0
- package/crates/tish_eval/src/regex.rs +299 -0
- package/crates/tish_eval/src/timers.rs +120 -0
- package/crates/tish_eval/src/value.rs +318 -0
- package/crates/tish_eval/src/value_convert.rs +111 -0
- package/crates/tish_fmt/Cargo.toml +16 -0
- package/crates/tish_fmt/src/bin/tish-fmt.rs +41 -0
- package/crates/tish_fmt/src/lib.rs +2101 -0
- package/crates/tish_jsx_web/Cargo.toml +9 -0
- package/crates/tish_jsx_web/README.md +5 -0
- package/crates/tish_jsx_web/src/lib.rs +2 -0
- package/crates/tish_lexer/Cargo.toml +9 -0
- package/crates/tish_lexer/src/lib.rs +716 -0
- package/crates/tish_lexer/src/token.rs +163 -0
- package/crates/tish_lint/Cargo.toml +18 -0
- package/crates/tish_lint/src/bin/tish-lint.rs +195 -0
- package/crates/tish_lint/src/lib.rs +289 -0
- package/crates/tish_llvm/Cargo.toml +13 -0
- package/crates/tish_llvm/src/lib.rs +115 -0
- package/crates/tish_lsp/Cargo.toml +25 -0
- package/crates/tish_lsp/README.md +26 -0
- package/crates/tish_lsp/src/builtin_goto.rs +362 -0
- package/crates/tish_lsp/src/import_goto.rs +562 -0
- package/crates/tish_lsp/src/main.rs +1046 -0
- package/crates/tish_native/Cargo.toml +16 -0
- package/crates/tish_native/src/build.rs +427 -0
- package/crates/tish_native/src/config.rs +48 -0
- package/crates/tish_native/src/lib.rs +416 -0
- package/crates/tish_opt/Cargo.toml +13 -0
- package/crates/tish_opt/src/lib.rs +943 -0
- package/crates/tish_parser/Cargo.toml +11 -0
- package/crates/tish_parser/src/lib.rs +332 -0
- package/crates/tish_parser/src/parser.rs +2304 -0
- package/crates/tish_pg/Cargo.toml +34 -0
- package/crates/tish_pg/README.md +38 -0
- package/crates/tish_pg/src/error.rs +52 -0
- package/crates/tish_pg/src/lib.rs +955 -0
- package/crates/tish_resolve/Cargo.toml +13 -0
- package/crates/tish_resolve/src/lib.rs +3561 -0
- package/crates/tish_resolve/src/pos.rs +141 -0
- package/crates/tish_runtime/Cargo.toml +96 -0
- package/crates/tish_runtime/src/http.rs +1298 -0
- package/crates/tish_runtime/src/http_fetch.rs +471 -0
- package/crates/tish_runtime/src/http_hyper.rs +418 -0
- package/crates/tish_runtime/src/http_prefork.rs +189 -0
- package/crates/tish_runtime/src/lib.rs +1192 -0
- package/crates/tish_runtime/src/native_promise.rs +15 -0
- package/crates/tish_runtime/src/promise.rs +248 -0
- package/crates/tish_runtime/src/promise_io.rs +38 -0
- package/crates/tish_runtime/src/timers.rs +166 -0
- package/crates/tish_runtime/src/ws.rs +761 -0
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +102 -0
- package/crates/tish_ui/Cargo.toml +17 -0
- package/crates/tish_ui/src/jsx.rs +682 -0
- package/crates/tish_ui/src/lib.rs +20 -0
- package/crates/tish_ui/src/runtime/hooks.rs +569 -0
- package/crates/tish_ui/src/runtime/mod.rs +180 -0
- package/crates/tish_vm/Cargo.toml +47 -0
- package/crates/tish_vm/src/lib.rs +39 -0
- package/crates/tish_vm/src/vm.rs +2192 -0
- package/crates/tish_vm/tests/fixtures/or_string_cmd.tish +2 -0
- package/crates/tish_vm/tests/lexical_scope_declare.rs +34 -0
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +150 -0
- package/crates/tish_wasm/Cargo.toml +15 -0
- package/crates/tish_wasm/src/lib.rs +424 -0
- package/crates/tish_wasm_runtime/Cargo.toml +37 -0
- package/crates/tish_wasm_runtime/src/gpu.rs +413 -0
- package/crates/tish_wasm_runtime/src/lib.rs +42 -0
- package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +263 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +125 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +382 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +167 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +117 -0
- package/justfile +268 -0
- package/package.json +1 -1
- package/platform/darwin-arm64/tish-fmt +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//! `DeclareVar` + block scopes: function `let` shadows script-level names (bytecode VM).
|
|
2
|
+
|
|
3
|
+
use tishlang_bytecode::compile;
|
|
4
|
+
use tishlang_vm::run;
|
|
5
|
+
|
|
6
|
+
#[test]
|
|
7
|
+
fn declare_var_shadows_script_let_inside_fn() {
|
|
8
|
+
let src = r#"
|
|
9
|
+
let x = 1
|
|
10
|
+
fn f() {
|
|
11
|
+
let x = 2
|
|
12
|
+
return x
|
|
13
|
+
}
|
|
14
|
+
let r = f()
|
|
15
|
+
console.log("script", x, "fn", r)
|
|
16
|
+
"#;
|
|
17
|
+
let program = tishlang_parser::parse(src).expect("parse");
|
|
18
|
+
let chunk = compile(&program).expect("compile");
|
|
19
|
+
run(&chunk).expect("run");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#[test]
|
|
23
|
+
fn block_let_restores_outer_binding() {
|
|
24
|
+
let src = r#"
|
|
25
|
+
let x = 1
|
|
26
|
+
{
|
|
27
|
+
let x = 2
|
|
28
|
+
}
|
|
29
|
+
console.log(x)
|
|
30
|
+
"#;
|
|
31
|
+
let program = tishlang_parser::parse(src).expect("parse");
|
|
32
|
+
let chunk = compile(&program).expect("compile");
|
|
33
|
+
run(&chunk).expect("run");
|
|
34
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
//! Regression: bytecode peephole `chain_jumps` must not follow `JumpIfFalse` as if it were an
|
|
2
|
+
//! unconditional `Jump`. Doing so broke `===` + `||` when nested as the condition of an outer `if`
|
|
3
|
+
//! (default VM differed from `--backend interp` / `--no-optimize`).
|
|
4
|
+
//!
|
|
5
|
+
//! CLI parity for the same source is covered in `crates/tish/tests/run_optimize_stdout_parity.rs`.
|
|
6
|
+
|
|
7
|
+
use std::path::PathBuf;
|
|
8
|
+
|
|
9
|
+
use tishlang_bytecode::{
|
|
10
|
+
compile, compile_for_repl, compile_for_repl_unoptimized, compile_unoptimized,
|
|
11
|
+
};
|
|
12
|
+
use tishlang_core::Value;
|
|
13
|
+
|
|
14
|
+
fn run_chunk(chunk: &tishlang_bytecode::Chunk) -> Value {
|
|
15
|
+
tishlang_vm::run(chunk).expect("vm run")
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// `tish run` ends with trailing `null` when the last statement is not a REPL-style expr; use
|
|
19
|
+
/// `compile_for_repl` so the VM return value reflects the `||` result (catches peephole/AST bugs).
|
|
20
|
+
#[test]
|
|
21
|
+
fn string_strict_eq_logical_or_repl_last_expr_is_true() {
|
|
22
|
+
let src = "let cmd = \"a\"\ncmd === \"a\" || cmd === \"b\"";
|
|
23
|
+
let opt = tishlang_opt::optimize(&tishlang_parser::parse(src).expect("parse"));
|
|
24
|
+
let v_peep = run_chunk(&compile_for_repl(&opt).expect("compile repl"));
|
|
25
|
+
let v_unopt = run_chunk(&compile_for_repl_unoptimized(&opt).expect("compile repl unopt"));
|
|
26
|
+
assert!(
|
|
27
|
+
v_peep.strict_eq(&v_unopt),
|
|
28
|
+
"peephole vs unopt repl: peep={v_peep:?} unopt={v_unopt:?}"
|
|
29
|
+
);
|
|
30
|
+
assert!(
|
|
31
|
+
matches!(&v_peep, Value::Bool(true)),
|
|
32
|
+
"expected true for cmd===a||cmd===b with cmd=a, got {v_peep:?}"
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// `?:` uses different codegen than `if`; both must agree with unoptimized bytecode.
|
|
37
|
+
#[test]
|
|
38
|
+
fn string_strict_eq_logical_or_inside_ternary_repl_last_expr() {
|
|
39
|
+
// Statement boundary: without `;` or `;`-like ASI, the parser can tie the `(` line to `let`.
|
|
40
|
+
let src = "let cmd = \"a\"\n;(cmd === \"a\" || cmd === \"b\") ? 1 : 0";
|
|
41
|
+
let opt = tishlang_opt::optimize(&tishlang_parser::parse(src).expect("parse"));
|
|
42
|
+
let v_peep = run_chunk(&compile_for_repl(&opt).expect("compile repl"));
|
|
43
|
+
let v_unopt = run_chunk(&compile_for_repl_unoptimized(&opt).expect("compile repl unopt"));
|
|
44
|
+
assert!(
|
|
45
|
+
v_peep.strict_eq(&v_unopt),
|
|
46
|
+
"peep={v_peep:?} unopt={v_unopt:?}"
|
|
47
|
+
);
|
|
48
|
+
assert!(
|
|
49
|
+
matches!(&v_peep, Value::Number(n) if *n == 1.0),
|
|
50
|
+
"expected 1, got {v_peep:?}"
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
#[test]
|
|
55
|
+
fn logical_or_strict_eq_peephole_matches_unoptimized() {
|
|
56
|
+
let src = "let a = 1\nlet b = 2\na === 1 || b === 2";
|
|
57
|
+
let program = tishlang_parser::parse(src).expect("parse");
|
|
58
|
+
let program = tishlang_opt::optimize(&program);
|
|
59
|
+
|
|
60
|
+
let v_peep = run_chunk(&compile(&program).expect("compile"));
|
|
61
|
+
let v_raw = run_chunk(&compile_unoptimized(&program).expect("compile unopt"));
|
|
62
|
+
assert!(
|
|
63
|
+
v_peep.strict_eq(&v_raw),
|
|
64
|
+
"peephole changed semantics: peep={v_peep:?} raw={v_raw:?}"
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
let v_peep_repl = run_chunk(&compile_for_repl(&program).expect("compile repl"));
|
|
68
|
+
let v_raw_repl =
|
|
69
|
+
run_chunk(&compile_for_repl_unoptimized(&program).expect("compile repl unopt"));
|
|
70
|
+
assert!(
|
|
71
|
+
v_peep_repl.strict_eq(&v_raw_repl),
|
|
72
|
+
"repl: peep={v_peep_repl:?} raw={v_raw_repl:?}"
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
#[test]
|
|
77
|
+
fn logical_or_inside_if_condition_peephole_matches_unoptimized() {
|
|
78
|
+
let src = "let a = 1\nlet b = 2\nif (a === 1 || b === 2) { 1 } else { 0 }";
|
|
79
|
+
let program = tishlang_parser::parse(src).expect("parse");
|
|
80
|
+
let program = tishlang_opt::optimize(&program);
|
|
81
|
+
|
|
82
|
+
let v_peep = run_chunk(&compile(&program).expect("compile"));
|
|
83
|
+
let v_raw = run_chunk(&compile_unoptimized(&program).expect("compile unopt"));
|
|
84
|
+
assert!(
|
|
85
|
+
v_peep.strict_eq(&v_raw),
|
|
86
|
+
"if + || : peep={v_peep:?} raw={v_raw:?}"
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
#[test]
|
|
91
|
+
fn string_strict_eq_logical_or_ast_opt_matches_unoptimized_bytecode() {
|
|
92
|
+
let src = "let cmd = \"a\"\nif (cmd === \"a\" || cmd === \"b\") { 1 } else { 0 }";
|
|
93
|
+
let raw = tishlang_parser::parse(src).expect("parse");
|
|
94
|
+
let opt = tishlang_opt::optimize(&raw);
|
|
95
|
+
let v_raw = run_chunk(&compile_unoptimized(&raw).expect("raw"));
|
|
96
|
+
let v_opt = run_chunk(&compile_unoptimized(&opt).expect("opt"));
|
|
97
|
+
assert!(
|
|
98
|
+
v_raw.strict_eq(&v_opt),
|
|
99
|
+
"AST optimizer changed semantics: raw={v_raw:?} opt={v_opt:?}"
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
#[test]
|
|
104
|
+
fn string_strict_eq_logical_or_peephole_matches_unoptimized() {
|
|
105
|
+
let src = "let cmd = \"a\"\nif (cmd === \"a\" || cmd === \"b\") { 1 } else { 0 }";
|
|
106
|
+
let program = tishlang_opt::optimize(&tishlang_parser::parse(src).expect("parse"));
|
|
107
|
+
let v_peep = run_chunk(&compile(&program).expect("compile"));
|
|
108
|
+
let v_raw = run_chunk(&compile_unoptimized(&program).expect("unopt"));
|
|
109
|
+
assert!(
|
|
110
|
+
v_peep.strict_eq(&v_raw),
|
|
111
|
+
"peephole + strings: peep={v_peep:?} raw={v_raw:?}"
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/// `tish run path/to/file.tish` uses merge_modules; ensure that matches plain parse for the fixture.
|
|
116
|
+
#[test]
|
|
117
|
+
fn merged_module_program_bytecode_matches_parse_for_string_or_fixture() {
|
|
118
|
+
let fixture =
|
|
119
|
+
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/or_string_cmd.tish");
|
|
120
|
+
let src = std::fs::read_to_string(&fixture).expect("read fixture");
|
|
121
|
+
let modules = tishlang_compile::resolve_project(&fixture, Some(fixture.parent().unwrap()))
|
|
122
|
+
.expect("resolve");
|
|
123
|
+
let merged = tishlang_compile::merge_modules(modules)
|
|
124
|
+
.expect("merge")
|
|
125
|
+
.program;
|
|
126
|
+
let flat = tishlang_parser::parse(&src).expect("parse");
|
|
127
|
+
let m_opt = tishlang_opt::optimize(&merged);
|
|
128
|
+
let f_opt = tishlang_opt::optimize(&flat);
|
|
129
|
+
let c_m = compile(&m_opt).expect("compile merged");
|
|
130
|
+
let c_f = compile(&f_opt).expect("compile flat");
|
|
131
|
+
assert_eq!(
|
|
132
|
+
c_m.code, c_f.code,
|
|
133
|
+
"merge_modules vs parse produced different bytecode"
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/// `if (cmd === "a" || cmd === "b")` must match unoptimized VM semantics (Nop padding from other
|
|
138
|
+
/// peepholes must not confuse `chain_jumps`).
|
|
139
|
+
#[test]
|
|
140
|
+
fn string_eq_or_in_if_stmt_matches_unoptimized_repl() {
|
|
141
|
+
let src = "let cmd = \"a\"\nlet ok = false\nif (cmd === \"a\" || cmd === \"b\") { ok = true } else { ok = false }\nok";
|
|
142
|
+
let program = tishlang_opt::optimize(&tishlang_parser::parse(src).expect("parse"));
|
|
143
|
+
let v_peep = run_chunk(&compile_for_repl(&program).expect("compile repl"));
|
|
144
|
+
let v_raw = run_chunk(&compile_for_repl_unoptimized(&program).expect("compile repl unopt"));
|
|
145
|
+
assert!(v_peep.strict_eq(&v_raw), "peep={v_peep:?} raw={v_raw:?}");
|
|
146
|
+
assert!(
|
|
147
|
+
matches!(&v_peep, Value::Bool(true)),
|
|
148
|
+
"expected ok=true, got {v_peep:?}"
|
|
149
|
+
);
|
|
150
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "tishlang_wasm"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
description = "WebAssembly backend for Tish - compiles to real .wasm"
|
|
6
|
+
|
|
7
|
+
license-file = { workspace = true }
|
|
8
|
+
repository = { workspace = true }
|
|
9
|
+
[dependencies]
|
|
10
|
+
tishlang_build_utils = { path = "../tish_build_utils", version = ">=0.1" }
|
|
11
|
+
tishlang_ast = { path = "../tish_ast", version = ">=0.1" }
|
|
12
|
+
tishlang_compile = { path = "../tish_compile", version = ">=0.1" }
|
|
13
|
+
tishlang_bytecode = { path = "../tish_bytecode", version = ">=0.1" }
|
|
14
|
+
tishlang_opt = { path = "../tish_opt", version = ">=0.1" }
|
|
15
|
+
base64 = "0.22"
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
//! WebAssembly backend for Tish.
|
|
2
|
+
//!
|
|
3
|
+
//! Compiles Tish to bytecode, then produces a .wasm VM binary + loader.
|
|
4
|
+
//! The VM runs in the browser; your program runs as serialized bytecode.
|
|
5
|
+
|
|
6
|
+
use std::collections::BTreeSet;
|
|
7
|
+
use std::path::Path;
|
|
8
|
+
use std::process::Command;
|
|
9
|
+
|
|
10
|
+
use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
|
|
11
|
+
|
|
12
|
+
use tishlang_ast::Program;
|
|
13
|
+
use tishlang_bytecode::{serialize, Chunk};
|
|
14
|
+
use tishlang_compile::{
|
|
15
|
+
detect_cycles, extract_native_import_features, has_external_native_imports, merge_modules,
|
|
16
|
+
resolve_project,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/// Error from WASM compilation.
|
|
20
|
+
#[derive(Debug)]
|
|
21
|
+
pub struct WasmError {
|
|
22
|
+
pub message: String,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
impl std::fmt::Display for WasmError {
|
|
26
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
27
|
+
write!(f, "{}", self.message)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
impl std::error::Error for WasmError {}
|
|
32
|
+
|
|
33
|
+
/// Map CLI / import capability names to `tishlang_wasm_runtime` Cargo features for wasm32-wasip1.
|
|
34
|
+
/// The full `http` stack (tokio/socket2/…) does not build on WASI here; `http` maps to `promise`
|
|
35
|
+
/// so `Promise` / `await` work. `ws` is skipped for the same reason.
|
|
36
|
+
fn insert_wasi_runtime_cap(out: &mut BTreeSet<String>, cap: &str) {
|
|
37
|
+
match cap {
|
|
38
|
+
"http" => {
|
|
39
|
+
out.insert("promise".to_string());
|
|
40
|
+
}
|
|
41
|
+
"ws" => {}
|
|
42
|
+
"fs" | "process" | "promise" | "timers" | "regex" => {
|
|
43
|
+
out.insert(cap.to_string());
|
|
44
|
+
}
|
|
45
|
+
_ => {}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// Resolve project, merge modules, and compile to bytecode chunk.
|
|
50
|
+
/// Returns (Chunk, Program) so WASI can extract features for the runtime.
|
|
51
|
+
fn resolve_and_compile_to_chunk(
|
|
52
|
+
entry_path: &Path,
|
|
53
|
+
project_root: Option<&Path>,
|
|
54
|
+
optimize: bool,
|
|
55
|
+
) -> Result<(Chunk, Program), WasmError> {
|
|
56
|
+
let modules = resolve_project(entry_path, project_root).map_err(|e| WasmError {
|
|
57
|
+
message: e.to_string(),
|
|
58
|
+
})?;
|
|
59
|
+
detect_cycles(&modules).map_err(|e| WasmError {
|
|
60
|
+
message: e.to_string(),
|
|
61
|
+
})?;
|
|
62
|
+
let program = {
|
|
63
|
+
let prog = merge_modules(modules)
|
|
64
|
+
.map(|m| m.program)
|
|
65
|
+
.map_err(|e| WasmError {
|
|
66
|
+
message: e.to_string(),
|
|
67
|
+
})?;
|
|
68
|
+
if optimize {
|
|
69
|
+
tishlang_opt::optimize(&prog)
|
|
70
|
+
} else {
|
|
71
|
+
prog
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
let chunk = if optimize {
|
|
75
|
+
tishlang_bytecode::compile(&program).map_err(|e| WasmError {
|
|
76
|
+
message: e.to_string(),
|
|
77
|
+
})?
|
|
78
|
+
} else {
|
|
79
|
+
tishlang_bytecode::compile_unoptimized(&program).map_err(|e| WasmError {
|
|
80
|
+
message: e.to_string(),
|
|
81
|
+
})?
|
|
82
|
+
};
|
|
83
|
+
Ok((chunk, program))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// Compile a single Program (e.g. from tishlang_js_to_tish) for WebAssembly.
|
|
87
|
+
pub fn compile_program_to_wasm(
|
|
88
|
+
program: &Program,
|
|
89
|
+
output_path: &Path,
|
|
90
|
+
optimize: bool,
|
|
91
|
+
) -> Result<(), WasmError> {
|
|
92
|
+
let program = if optimize {
|
|
93
|
+
tishlang_opt::optimize(program)
|
|
94
|
+
} else {
|
|
95
|
+
program.clone()
|
|
96
|
+
};
|
|
97
|
+
let chunk = if optimize {
|
|
98
|
+
tishlang_bytecode::compile(&program).map_err(|e| WasmError {
|
|
99
|
+
message: e.to_string(),
|
|
100
|
+
})?
|
|
101
|
+
} else {
|
|
102
|
+
tishlang_bytecode::compile_unoptimized(&program).map_err(|e| WasmError {
|
|
103
|
+
message: e.to_string(),
|
|
104
|
+
})?
|
|
105
|
+
};
|
|
106
|
+
emit_wasm_from_chunk(&chunk, output_path)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
fn emit_wasm_from_chunk(chunk: &Chunk, output_path: &Path) -> Result<(), WasmError> {
|
|
110
|
+
let chunk_bytes = serialize(chunk);
|
|
111
|
+
let chunk_b64 = BASE64.encode(&chunk_bytes);
|
|
112
|
+
let stem = output_path
|
|
113
|
+
.file_stem()
|
|
114
|
+
.and_then(|s| s.to_str())
|
|
115
|
+
.unwrap_or("main");
|
|
116
|
+
let out_dir = output_path
|
|
117
|
+
.parent()
|
|
118
|
+
.filter(|p| !p.as_os_str().is_empty())
|
|
119
|
+
.unwrap_or(Path::new("."));
|
|
120
|
+
let out_dir_abs = out_dir
|
|
121
|
+
.canonicalize()
|
|
122
|
+
.or_else(|_| std::env::current_dir().map(|cwd| cwd.join(out_dir)))
|
|
123
|
+
.map_err(|e| WasmError {
|
|
124
|
+
message: format!("Cannot resolve output dir: {}", e),
|
|
125
|
+
})?;
|
|
126
|
+
std::fs::create_dir_all(&out_dir_abs).map_err(|e| WasmError {
|
|
127
|
+
message: format!("Cannot create output directory: {}", e),
|
|
128
|
+
})?;
|
|
129
|
+
let workspace_root =
|
|
130
|
+
tishlang_build_utils::find_workspace_root().map_err(|e| WasmError { message: e })?;
|
|
131
|
+
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
|
|
132
|
+
let build_status = Command::new(&cargo)
|
|
133
|
+
.current_dir(&workspace_root)
|
|
134
|
+
.args([
|
|
135
|
+
"build",
|
|
136
|
+
"-p",
|
|
137
|
+
"tishlang_wasm_runtime",
|
|
138
|
+
"--target",
|
|
139
|
+
"wasm32-unknown-unknown",
|
|
140
|
+
"--release",
|
|
141
|
+
"--features",
|
|
142
|
+
"browser",
|
|
143
|
+
])
|
|
144
|
+
.status()
|
|
145
|
+
.map_err(|e| WasmError {
|
|
146
|
+
message: format!("Failed to run cargo: {}", e),
|
|
147
|
+
})?;
|
|
148
|
+
if !build_status.success() {
|
|
149
|
+
return Err(WasmError {
|
|
150
|
+
message: "Failed to build wasm runtime. Run: rustup target add wasm32-unknown-unknown"
|
|
151
|
+
.to_string(),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
let wasm_artifact =
|
|
155
|
+
workspace_root.join("target/wasm32-unknown-unknown/release/tishlang_wasm_runtime.wasm");
|
|
156
|
+
if !wasm_artifact.exists() {
|
|
157
|
+
return Err(WasmError {
|
|
158
|
+
message: format!("Wasm artifact not found: {}", wasm_artifact.display()),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
let wasm_bindgen = std::env::var("WASM_BINDGEN").unwrap_or_else(|_| "wasm-bindgen".to_string());
|
|
162
|
+
let out_name = stem.to_string();
|
|
163
|
+
let bindgen_status = Command::new(&wasm_bindgen)
|
|
164
|
+
.args([
|
|
165
|
+
"--target",
|
|
166
|
+
"web",
|
|
167
|
+
"--out-dir",
|
|
168
|
+
out_dir_abs.to_str().unwrap(),
|
|
169
|
+
"--out-name",
|
|
170
|
+
&out_name,
|
|
171
|
+
wasm_artifact.to_str().unwrap(),
|
|
172
|
+
])
|
|
173
|
+
.status()
|
|
174
|
+
.map_err(|e| WasmError {
|
|
175
|
+
message: format!(
|
|
176
|
+
"Failed to run wasm-bindgen: {}. Install with: cargo install wasm-bindgen-cli",
|
|
177
|
+
e
|
|
178
|
+
),
|
|
179
|
+
})?;
|
|
180
|
+
if !bindgen_status.success() {
|
|
181
|
+
return Err(WasmError {
|
|
182
|
+
message: "wasm-bindgen failed".to_string(),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
let js_name = format!("{}.js", stem);
|
|
186
|
+
let html = format!(
|
|
187
|
+
r#"<!DOCTYPE html>
|
|
188
|
+
<html>
|
|
189
|
+
<head><meta charset="utf-8"><title>{}</title></head>
|
|
190
|
+
<body>
|
|
191
|
+
<script type="module">
|
|
192
|
+
const CHUNK_B64 = "{}";
|
|
193
|
+
const chunk = Uint8Array.from(atob(CHUNK_B64), c => c.charCodeAt(0));
|
|
194
|
+
import init, {{ run }} from './{}';
|
|
195
|
+
await init();
|
|
196
|
+
run(chunk);
|
|
197
|
+
</script>
|
|
198
|
+
</body>
|
|
199
|
+
</html>
|
|
200
|
+
"#,
|
|
201
|
+
stem, chunk_b64, js_name
|
|
202
|
+
);
|
|
203
|
+
let html_path = out_dir_abs.join(format!("{}.html", stem));
|
|
204
|
+
std::fs::write(&html_path, html).map_err(|e| WasmError {
|
|
205
|
+
message: format!("Cannot write {}: {}", html_path.display(), e),
|
|
206
|
+
})?;
|
|
207
|
+
println!(
|
|
208
|
+
"Built: {}_bg.wasm, {}.js, {}",
|
|
209
|
+
stem,
|
|
210
|
+
stem,
|
|
211
|
+
html_path.display()
|
|
212
|
+
);
|
|
213
|
+
Ok(())
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/// Compile a Tish project for WebAssembly.
|
|
217
|
+
///
|
|
218
|
+
/// Produces:
|
|
219
|
+
/// - `{output}.wasm` — VM binary (runs your program)
|
|
220
|
+
/// - `{output}.js` — wasm-bindgen glue
|
|
221
|
+
/// - `{output}.html` — loader (open in browser)
|
|
222
|
+
///
|
|
223
|
+
/// Requires: `rustup target add wasm32-unknown-unknown`, `wasm-bindgen-cli`
|
|
224
|
+
pub fn compile_to_wasm(
|
|
225
|
+
entry_path: &Path,
|
|
226
|
+
project_root: Option<&Path>,
|
|
227
|
+
output_path: &Path,
|
|
228
|
+
optimize: bool,
|
|
229
|
+
) -> Result<(), WasmError> {
|
|
230
|
+
let (chunk, _) = resolve_and_compile_to_chunk(entry_path, project_root, optimize)?;
|
|
231
|
+
emit_wasm_from_chunk(&chunk, output_path)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/// Compile a Tish project to a raw serialized bytecode chunk.
|
|
235
|
+
///
|
|
236
|
+
/// Writes a single `{output}` file of the exact bytes that the wasm/WASI runtime entry points
|
|
237
|
+
/// (`start` / `run`) deserialize directly — the same chunk `--target wasm` embeds as base64 in
|
|
238
|
+
/// its generated HTML loader, but written raw with no VM binary, JS glue, or HTML wrapper. Lets a
|
|
239
|
+
/// host that already ships the VM runtime (e.g. a bundler) consume the bytecode without the
|
|
240
|
+
/// throwaway standalone build.
|
|
241
|
+
pub fn compile_to_bytecode(
|
|
242
|
+
entry_path: &Path,
|
|
243
|
+
project_root: Option<&Path>,
|
|
244
|
+
output_path: &Path,
|
|
245
|
+
optimize: bool,
|
|
246
|
+
) -> Result<(), WasmError> {
|
|
247
|
+
let (chunk, _) = resolve_and_compile_to_chunk(entry_path, project_root, optimize)?;
|
|
248
|
+
let bytes = serialize(&chunk);
|
|
249
|
+
if let Some(parent) = output_path.parent().filter(|p| !p.as_os_str().is_empty()) {
|
|
250
|
+
std::fs::create_dir_all(parent).map_err(|e| WasmError {
|
|
251
|
+
message: format!("Cannot create output directory: {}", e),
|
|
252
|
+
})?;
|
|
253
|
+
}
|
|
254
|
+
std::fs::write(output_path, &bytes).map_err(|e| WasmError {
|
|
255
|
+
message: format!("Cannot write {}: {}", output_path.display(), e),
|
|
256
|
+
})?;
|
|
257
|
+
println!("Built: {} ({} bytes)", output_path.display(), bytes.len());
|
|
258
|
+
Ok(())
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/// Compile a Tish project for Wasmtime/WASI.
|
|
262
|
+
///
|
|
263
|
+
/// Produces a single `{output}.wasm` with embedded bytecode. Run with:
|
|
264
|
+
/// `wasmtime {output}.wasm`
|
|
265
|
+
///
|
|
266
|
+
/// Requires: `rustup target add wasm32-wasip1`
|
|
267
|
+
///
|
|
268
|
+
/// `capabilities` is the same capability list as `tish build --target native` (e.g. from
|
|
269
|
+
/// `native_build_features_from_cli`): merged with `import`-inferred features so globals like
|
|
270
|
+
/// `Promise` / `fetch` work without a top-level `import … from 'http'`.
|
|
271
|
+
pub fn compile_to_wasi(
|
|
272
|
+
entry_path: &Path,
|
|
273
|
+
project_root: Option<&Path>,
|
|
274
|
+
output_path: &Path,
|
|
275
|
+
optimize: bool,
|
|
276
|
+
capabilities: &[String],
|
|
277
|
+
) -> Result<(), WasmError> {
|
|
278
|
+
let (chunk, program) = resolve_and_compile_to_chunk(entry_path, project_root, optimize)?;
|
|
279
|
+
if has_external_native_imports(&program) {
|
|
280
|
+
return Err(WasmError {
|
|
281
|
+
message: "WASI backend does not support external native imports (tish:egui, @scope/pkg). Built-in tish:fs, tish:http, tish:process, tish:timers are supported.".to_string(),
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
let mut wasi_feature_set: BTreeSet<String> = BTreeSet::new();
|
|
285
|
+
for f in extract_native_import_features(&program) {
|
|
286
|
+
insert_wasi_runtime_cap(&mut wasi_feature_set, f.as_str());
|
|
287
|
+
}
|
|
288
|
+
for f in capabilities {
|
|
289
|
+
insert_wasi_runtime_cap(&mut wasi_feature_set, f.as_str());
|
|
290
|
+
}
|
|
291
|
+
// Many scripts use global setTimeout without `import` from timers.
|
|
292
|
+
wasi_feature_set.insert("timers".to_string());
|
|
293
|
+
|
|
294
|
+
let chunk_bytes = serialize(&chunk);
|
|
295
|
+
|
|
296
|
+
let stem = output_path
|
|
297
|
+
.file_stem()
|
|
298
|
+
.and_then(|s| s.to_str())
|
|
299
|
+
.unwrap_or("main");
|
|
300
|
+
let out_dir = output_path
|
|
301
|
+
.parent()
|
|
302
|
+
.filter(|p| !p.as_os_str().is_empty())
|
|
303
|
+
.unwrap_or(Path::new("."));
|
|
304
|
+
let out_dir_abs = out_dir
|
|
305
|
+
.canonicalize()
|
|
306
|
+
.or_else(|_| std::env::current_dir().map(|cwd| cwd.join(out_dir)))
|
|
307
|
+
.map_err(|e| WasmError {
|
|
308
|
+
message: format!("Cannot resolve output dir: {}", e),
|
|
309
|
+
})?;
|
|
310
|
+
|
|
311
|
+
std::fs::create_dir_all(&out_dir_abs).map_err(|e| WasmError {
|
|
312
|
+
message: format!("Cannot create output directory: {}", e),
|
|
313
|
+
})?;
|
|
314
|
+
|
|
315
|
+
let workspace_root =
|
|
316
|
+
tishlang_build_utils::find_workspace_root().map_err(|e| WasmError { message: e })?;
|
|
317
|
+
|
|
318
|
+
// Create generated project: wasi_build/{stem}/
|
|
319
|
+
let build_dir = out_dir_abs.join("wasi_build").join(stem);
|
|
320
|
+
std::fs::create_dir_all(build_dir.join("src")).map_err(|e| WasmError {
|
|
321
|
+
message: format!("Cannot create build dir: {}", e),
|
|
322
|
+
})?;
|
|
323
|
+
|
|
324
|
+
// Write chunk.bin
|
|
325
|
+
std::fs::write(build_dir.join("chunk.bin"), &chunk_bytes).map_err(|e| WasmError {
|
|
326
|
+
message: format!("Cannot write chunk: {}", e),
|
|
327
|
+
})?;
|
|
328
|
+
|
|
329
|
+
// Cargo.toml - path to tishlang_wasm_runtime (crate in crates/tish_wasm_runtime)
|
|
330
|
+
let runtime_path = workspace_root.join("crates").join("tish_wasm_runtime");
|
|
331
|
+
let runtime_path_str = runtime_path
|
|
332
|
+
.canonicalize()
|
|
333
|
+
.unwrap_or(runtime_path)
|
|
334
|
+
.to_string_lossy()
|
|
335
|
+
.replace('\\', "/");
|
|
336
|
+
|
|
337
|
+
let features_str = format!(
|
|
338
|
+
", features = [{}]",
|
|
339
|
+
wasi_feature_set
|
|
340
|
+
.iter()
|
|
341
|
+
.map(|f| format!("{:?}", f))
|
|
342
|
+
.collect::<Vec<_>>()
|
|
343
|
+
.join(", ")
|
|
344
|
+
);
|
|
345
|
+
let cargo_toml = format!(
|
|
346
|
+
r#"[package]
|
|
347
|
+
name = "tish_wasi_{stem}"
|
|
348
|
+
version = "0.1.0"
|
|
349
|
+
edition = "2021"
|
|
350
|
+
|
|
351
|
+
[workspace]
|
|
352
|
+
|
|
353
|
+
[[bin]]
|
|
354
|
+
name = "tish_wasi_{stem}"
|
|
355
|
+
path = "src/main.rs"
|
|
356
|
+
|
|
357
|
+
[dependencies]
|
|
358
|
+
tishlang_wasm_runtime = {{ path = "{runtime_path_str}"{features_str} }}
|
|
359
|
+
"#,
|
|
360
|
+
stem = stem,
|
|
361
|
+
runtime_path_str = runtime_path_str,
|
|
362
|
+
features_str = features_str
|
|
363
|
+
);
|
|
364
|
+
std::fs::write(build_dir.join("Cargo.toml"), cargo_toml).map_err(|e| WasmError {
|
|
365
|
+
message: format!("Cannot write Cargo.toml: {}", e),
|
|
366
|
+
})?;
|
|
367
|
+
|
|
368
|
+
// main.rs
|
|
369
|
+
let main_rs = r#"
|
|
370
|
+
fn main() {
|
|
371
|
+
let chunk = include_bytes!("../chunk.bin");
|
|
372
|
+
if let Err(e) = tishlang_wasm_runtime::run_wasi(chunk) {
|
|
373
|
+
eprintln!("Runtime error: {}", e);
|
|
374
|
+
std::process::exit(1);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
"#;
|
|
378
|
+
std::fs::write(build_dir.join("src").join("main.rs"), main_rs).map_err(|e| WasmError {
|
|
379
|
+
message: format!("Cannot write main.rs: {}", e),
|
|
380
|
+
})?;
|
|
381
|
+
|
|
382
|
+
// Build - use explicit target-dir so we know where the artifact is
|
|
383
|
+
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
|
|
384
|
+
let bin_name = format!("tish_wasi_{}", stem);
|
|
385
|
+
let target_dir = build_dir.join("target");
|
|
386
|
+
let build_status = Command::new(&cargo)
|
|
387
|
+
.current_dir(&build_dir)
|
|
388
|
+
.env("CARGO_TARGET_DIR", &target_dir)
|
|
389
|
+
.args(["build", "--target", "wasm32-wasip1", "--release"])
|
|
390
|
+
.status()
|
|
391
|
+
.map_err(|e| WasmError {
|
|
392
|
+
message: format!("Failed to run cargo: {}", e),
|
|
393
|
+
})?;
|
|
394
|
+
|
|
395
|
+
if !build_status.success() {
|
|
396
|
+
return Err(WasmError {
|
|
397
|
+
message: "Failed to build WASI binary. Run: rustup target add wasm32-wasip1"
|
|
398
|
+
.to_string(),
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
let wasm_artifact = target_dir
|
|
403
|
+
.join("wasm32-wasip1")
|
|
404
|
+
.join("release")
|
|
405
|
+
.join(format!("{}.wasm", bin_name));
|
|
406
|
+
|
|
407
|
+
if !wasm_artifact.exists() {
|
|
408
|
+
return Err(WasmError {
|
|
409
|
+
message: format!("WASI artifact not found: {}", wasm_artifact.display()),
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
let final_wasm = out_dir_abs.join(format!("{}.wasm", stem));
|
|
414
|
+
std::fs::copy(&wasm_artifact, &final_wasm).map_err(|e| WasmError {
|
|
415
|
+
message: format!("Cannot copy wasm: {}", e),
|
|
416
|
+
})?;
|
|
417
|
+
|
|
418
|
+
println!(
|
|
419
|
+
"Built: {} (run with: wasmtime {})",
|
|
420
|
+
final_wasm.display(),
|
|
421
|
+
final_wasm.display()
|
|
422
|
+
);
|
|
423
|
+
Ok(())
|
|
424
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "tishlang_wasm_runtime"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
description = "Tish VM compiled to WebAssembly (browser + Wasmtime/WASI)"
|
|
6
|
+
license-file = { workspace = true }
|
|
7
|
+
repository = { workspace = true }
|
|
8
|
+
|
|
9
|
+
[lib]
|
|
10
|
+
crate-type = ["cdylib", "rlib"]
|
|
11
|
+
|
|
12
|
+
[features]
|
|
13
|
+
# For wasm32-unknown-unknown (browser): wasm-bindgen, console output
|
|
14
|
+
browser = ["dep:wasm-bindgen", "tishlang_vm/wasm"]
|
|
15
|
+
# Browser WebGPU / JS-interop FFI + requestAnimationFrame render loop (the
|
|
16
|
+
# `start(chunk, env)` entry). Reflection-based bridge over js-sys; no web-sys
|
|
17
|
+
# WebGPU bindings needed since the WebGPU command API is synchronous.
|
|
18
|
+
gpu = ["browser", "dep:js-sys"]
|
|
19
|
+
# Built-in modules for WASI (wasm32-wasip1): align with `tishlang_cranelift_runtime` / CLI caps
|
|
20
|
+
fs = ["tishlang_vm/fs"]
|
|
21
|
+
process = ["tishlang_vm/process"]
|
|
22
|
+
http = ["tishlang_vm/http"]
|
|
23
|
+
promise = ["tishlang_vm/promise"]
|
|
24
|
+
timers = ["tishlang_vm/timers"]
|
|
25
|
+
regex = ["tishlang_vm/regex"]
|
|
26
|
+
ws = ["tishlang_vm/ws"]
|
|
27
|
+
|
|
28
|
+
[dependencies]
|
|
29
|
+
tishlang_bytecode = { path = "../tish_bytecode", version = ">=0.1" }
|
|
30
|
+
tishlang_vm = { path = "../tish_vm", version = ">=0.1" }
|
|
31
|
+
tishlang_core = { path = "../tish_core", version = ">=0.1" }
|
|
32
|
+
wasm-bindgen = { version = "0.2", optional = true }
|
|
33
|
+
js-sys = { version = "0.3", optional = true }
|
|
34
|
+
|
|
35
|
+
# rand_core → getrandom 0.4 needs wasm_js on wasm32-unknown-unknown (browser VM build).
|
|
36
|
+
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
|
37
|
+
getrandom = { version = "0.4", features = ["wasm_js"] }
|