@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,178 @@
|
|
|
1
|
+
//! Shared-mutable reference used by the Tish runtime for `Value::Array`,
|
|
2
|
+
//! `Value::Object`, and `Value::RegExp` payloads.
|
|
3
|
+
//!
|
|
4
|
+
//! ## Why this exists
|
|
5
|
+
//!
|
|
6
|
+
//! Tish's `Value` uses interior mutability for arrays, objects, and regex
|
|
7
|
+
//! state. Historically that was `Rc<RefCell<T>>`, which is fast but
|
|
8
|
+
//! `!Send` — so `Value` couldn't move across threads, which in turn meant
|
|
9
|
+
//! `serve(port, handler)` had to serialise every request through one
|
|
10
|
+
//! VM dispatcher thread.
|
|
11
|
+
//!
|
|
12
|
+
//! `VmRef<T>` lets the build system pick the right trade-off **per
|
|
13
|
+
//! compile target**:
|
|
14
|
+
//!
|
|
15
|
+
//! | feature `send-values` | `VmRef<T>` | `NativeFn` | targets |
|
|
16
|
+
//! |---------------------------|-------------------------|----------------------------------|--------------------------------------------------|
|
|
17
|
+
//! | **off** *(default)* | `Rc<RefCell<T>>` | `Rc<dyn Fn + 'static>` | wasm32, wasi, interpreter, cranelift/llvm VMs |
|
|
18
|
+
//! | **on** | `Arc<Mutex<T>>` | `Arc<dyn Fn + Send + Sync>` | Rust native with `http` enabled (server workloads) |
|
|
19
|
+
//!
|
|
20
|
+
//! The *API* is identical in both configurations (`borrow` / `borrow_mut`
|
|
21
|
+
//! / `ptr_eq` / `Clone`), so every existing call site in the workspace
|
|
22
|
+
//! compiles unchanged. What flips is only the underlying primitive.
|
|
23
|
+
//!
|
|
24
|
+
//! ## Why this matters for performance
|
|
25
|
+
//!
|
|
26
|
+
//! * **wasm / wasi / cranelift / llvm / interpreter**: still pure
|
|
27
|
+
//! `Rc<RefCell<T>>`. Zero atomic ops, no mutex churn, behaviour
|
|
28
|
+
//! bit-identical to the pre-migration baseline.
|
|
29
|
+
//! * **Rust native, non-server**: same — `send-values` only activates
|
|
30
|
+
//! when something in the dependency graph (usually `http`) needs it.
|
|
31
|
+
//! * **Rust native with server**: `Arc<Mutex<T>>` pays ~3–5 ns per
|
|
32
|
+
//! `borrow` in the uncontended case (single atomic CAS). On Tish's
|
|
33
|
+
//! hot paths — roughly 6–12 borrows per request — that's ~30–60 ns of
|
|
34
|
+
//! overhead. In exchange we get `N×` handler scaling across cores,
|
|
35
|
+
//! which recovers orders of magnitude more throughput than it costs.
|
|
36
|
+
//!
|
|
37
|
+
//! ## API surface
|
|
38
|
+
//!
|
|
39
|
+
//! ```ignore
|
|
40
|
+
//! let cell = VmRef::new(42);
|
|
41
|
+
//! *cell.borrow() + 1; // read
|
|
42
|
+
//! *cell.borrow_mut() = 99; // write
|
|
43
|
+
//! VmRef::ptr_eq(&a, &b); // identity
|
|
44
|
+
//! let clone = cell.clone(); // shared ownership
|
|
45
|
+
//! ```
|
|
46
|
+
//!
|
|
47
|
+
//! Returned guard types (`VmReadGuard<'_, T>`, `VmWriteGuard<'_, T>`) are
|
|
48
|
+
//! type aliases that pick `Ref`/`RefMut` or `MutexGuard` depending on the
|
|
49
|
+
//! feature. They both `Deref` (and, for write guards, `DerefMut`) to `T`
|
|
50
|
+
//! just like the underlying types.
|
|
51
|
+
|
|
52
|
+
use std::fmt;
|
|
53
|
+
|
|
54
|
+
// --------------------------------------------------------------------------
|
|
55
|
+
// Single-threaded backing store (default): Rc<RefCell<T>>
|
|
56
|
+
// --------------------------------------------------------------------------
|
|
57
|
+
#[cfg(not(feature = "send-values"))]
|
|
58
|
+
mod imp {
|
|
59
|
+
use std::cell::RefCell;
|
|
60
|
+
use std::rc::Rc;
|
|
61
|
+
|
|
62
|
+
#[derive(Default)]
|
|
63
|
+
pub struct VmRef<T: ?Sized>(pub(super) Rc<RefCell<T>>);
|
|
64
|
+
|
|
65
|
+
/// Read guard alias. On the single-threaded path this is a true
|
|
66
|
+
/// `Ref<'_, T>`, so multiple readers can coexist.
|
|
67
|
+
pub type ReadGuard<'a, T> = std::cell::Ref<'a, T>;
|
|
68
|
+
/// Write guard alias. Exclusive, `DerefMut`.
|
|
69
|
+
pub type WriteGuard<'a, T> = std::cell::RefMut<'a, T>;
|
|
70
|
+
|
|
71
|
+
impl<T> VmRef<T> {
|
|
72
|
+
#[inline]
|
|
73
|
+
pub fn new(value: T) -> Self {
|
|
74
|
+
VmRef(Rc::new(RefCell::new(value)))
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
impl<T: ?Sized> VmRef<T> {
|
|
79
|
+
#[inline]
|
|
80
|
+
pub fn borrow(&self) -> ReadGuard<'_, T> {
|
|
81
|
+
self.0.borrow()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
#[inline]
|
|
85
|
+
pub fn borrow_mut(&self) -> WriteGuard<'_, T> {
|
|
86
|
+
self.0.borrow_mut()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
#[inline]
|
|
90
|
+
pub fn ptr_eq(a: &Self, b: &Self) -> bool {
|
|
91
|
+
Rc::ptr_eq(&a.0, &b.0)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
#[inline]
|
|
95
|
+
pub fn strong_count(this: &Self) -> usize {
|
|
96
|
+
Rc::strong_count(&this.0)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
impl<T: ?Sized> Clone for VmRef<T> {
|
|
101
|
+
#[inline]
|
|
102
|
+
fn clone(&self) -> Self {
|
|
103
|
+
VmRef(Rc::clone(&self.0))
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// --------------------------------------------------------------------------
|
|
109
|
+
// Thread-safe backing store (opt-in): Arc<Mutex<T>>
|
|
110
|
+
// --------------------------------------------------------------------------
|
|
111
|
+
#[cfg(feature = "send-values")]
|
|
112
|
+
mod imp {
|
|
113
|
+
use std::sync::{Arc, Mutex};
|
|
114
|
+
|
|
115
|
+
#[derive(Default)]
|
|
116
|
+
pub struct VmRef<T: ?Sized>(pub(super) Arc<Mutex<T>>);
|
|
117
|
+
|
|
118
|
+
/// Read guard alias. On the multi-threaded path both readers and
|
|
119
|
+
/// writers share a single `MutexGuard` (exclusive access).
|
|
120
|
+
pub type ReadGuard<'a, T> = std::sync::MutexGuard<'a, T>;
|
|
121
|
+
/// Write guard alias.
|
|
122
|
+
pub type WriteGuard<'a, T> = std::sync::MutexGuard<'a, T>;
|
|
123
|
+
|
|
124
|
+
impl<T> VmRef<T> {
|
|
125
|
+
#[inline]
|
|
126
|
+
pub fn new(value: T) -> Self {
|
|
127
|
+
VmRef(Arc::new(Mutex::new(value)))
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
impl<T: ?Sized> VmRef<T> {
|
|
132
|
+
/// Acquire the inner mutex. Poisoning is swallowed — a Tish
|
|
133
|
+
/// handler panic already aborts the enclosing thread; there is
|
|
134
|
+
/// no invariant worth preserving past that point.
|
|
135
|
+
#[inline]
|
|
136
|
+
pub fn borrow(&self) -> ReadGuard<'_, T> {
|
|
137
|
+
self.0.lock().unwrap_or_else(|p| p.into_inner())
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
#[inline]
|
|
141
|
+
pub fn borrow_mut(&self) -> WriteGuard<'_, T> {
|
|
142
|
+
self.0.lock().unwrap_or_else(|p| p.into_inner())
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
#[inline]
|
|
146
|
+
pub fn ptr_eq(a: &Self, b: &Self) -> bool {
|
|
147
|
+
Arc::ptr_eq(&a.0, &b.0)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
#[inline]
|
|
151
|
+
pub fn strong_count(this: &Self) -> usize {
|
|
152
|
+
Arc::strong_count(&this.0)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
impl<T: ?Sized> Clone for VmRef<T> {
|
|
157
|
+
#[inline]
|
|
158
|
+
fn clone(&self) -> Self {
|
|
159
|
+
VmRef(Arc::clone(&self.0))
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
pub use imp::{ReadGuard as VmReadGuard, VmRef, WriteGuard as VmWriteGuard};
|
|
165
|
+
|
|
166
|
+
impl<T: fmt::Debug> fmt::Debug for VmRef<T> {
|
|
167
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
168
|
+
// Match `RefCell`'s debug format so snapshot-test output stays
|
|
169
|
+
// stable across the migration.
|
|
170
|
+
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
|
171
|
+
let guard = self.borrow();
|
|
172
|
+
format!("{:?}", &*guard)
|
|
173
|
+
})) {
|
|
174
|
+
Ok(s) => write!(f, "RefCell {{ value: {} }}", s),
|
|
175
|
+
Err(_) => write!(f, "RefCell {{ value: <borrowed> }}"),
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "tishlang_cranelift"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
description = "Bytecode to native via Cranelift"
|
|
6
|
+
|
|
7
|
+
license-file = { workspace = true }
|
|
8
|
+
repository = { workspace = true }
|
|
9
|
+
[dependencies]
|
|
10
|
+
tishlang_build_utils = { path = "../tish_build_utils", version = ">=0.1" }
|
|
11
|
+
cranelift = "0.130"
|
|
12
|
+
cranelift-codegen = "0.130"
|
|
13
|
+
cranelift-frontend = "0.130"
|
|
14
|
+
cranelift-module = "0.130"
|
|
15
|
+
cranelift-native = "0.130"
|
|
16
|
+
cranelift-object = "0.130"
|
|
17
|
+
target-lexicon = "0.13"
|
|
18
|
+
tishlang_bytecode = { path = "../tish_bytecode", version = ">=0.1" }
|
|
19
|
+
tishlang_core = { path = "../tish_core", version = ">=0.1" }
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//! Standalone native binary: embedded bytecode + VM (Cranelift used as object builder).
|
|
2
|
+
//!
|
|
3
|
+
//! Produces an executable that runs **`tishlang_vm`** on serialized bytecode embedded in
|
|
4
|
+
//! the binary — not lowering of Tish opcodes to CLIF/machine code (see module docs in
|
|
5
|
+
//! `lower.rs`). For Rust transpile + `tishlang_runtime`, use `--native-backend rust`.
|
|
6
|
+
|
|
7
|
+
mod link;
|
|
8
|
+
mod lower;
|
|
9
|
+
|
|
10
|
+
pub use link::link_to_binary;
|
|
11
|
+
|
|
12
|
+
use std::path::Path;
|
|
13
|
+
|
|
14
|
+
use tishlang_bytecode::Chunk;
|
|
15
|
+
|
|
16
|
+
/// Error from Cranelift compilation.
|
|
17
|
+
#[derive(Debug)]
|
|
18
|
+
pub struct CraneliftError {
|
|
19
|
+
pub message: String,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
impl std::fmt::Display for CraneliftError {
|
|
23
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
24
|
+
write!(f, "{}", self.message)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
impl std::error::Error for CraneliftError {}
|
|
29
|
+
|
|
30
|
+
/// Build a native binary that embeds `chunk` and runs it with the bytecode VM.
|
|
31
|
+
/// `features` are passed to `tishlang_cranelift_runtime` (e.g. fs, process, http).
|
|
32
|
+
pub fn compile_chunk_to_native(
|
|
33
|
+
chunk: &Chunk,
|
|
34
|
+
output_path: &Path,
|
|
35
|
+
features: &[String],
|
|
36
|
+
) -> Result<(), CraneliftError> {
|
|
37
|
+
let object_path = output_path.with_extension("o");
|
|
38
|
+
lower::lower_and_emit(chunk, &object_path)?;
|
|
39
|
+
link::link_to_binary(&object_path, output_path, features)?;
|
|
40
|
+
// Clean up .o file
|
|
41
|
+
let _ = std::fs::remove_file(&object_path);
|
|
42
|
+
Ok(())
|
|
43
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
//! Link object file with runtime to produce final binary.
|
|
2
|
+
//!
|
|
3
|
+
//! Uses Cargo to build a small binary that links our .o and runs the chunk.
|
|
4
|
+
|
|
5
|
+
use std::fs;
|
|
6
|
+
use std::path::Path;
|
|
7
|
+
|
|
8
|
+
use crate::CraneliftError;
|
|
9
|
+
|
|
10
|
+
pub fn link_to_binary(
|
|
11
|
+
object_path: &Path,
|
|
12
|
+
output_path: &Path,
|
|
13
|
+
features: &[String],
|
|
14
|
+
) -> Result<(), CraneliftError> {
|
|
15
|
+
let workspace_root =
|
|
16
|
+
tishlang_build_utils::find_workspace_root().map_err(|e| CraneliftError { message: e })?;
|
|
17
|
+
let out_name = output_path
|
|
18
|
+
.file_stem()
|
|
19
|
+
.and_then(|s| s.to_str())
|
|
20
|
+
.unwrap_or("tish_out");
|
|
21
|
+
let build_dir = tishlang_build_utils::create_build_dir("tishlang_cranelift_build", out_name)
|
|
22
|
+
.map_err(|e| CraneliftError { message: e })?;
|
|
23
|
+
|
|
24
|
+
let object_path_str = object_path
|
|
25
|
+
.canonicalize()
|
|
26
|
+
.map_err(|e| CraneliftError {
|
|
27
|
+
message: format!("Cannot canonicalize object path: {}", e),
|
|
28
|
+
})?
|
|
29
|
+
.display()
|
|
30
|
+
.to_string()
|
|
31
|
+
.replace('\\', "/");
|
|
32
|
+
|
|
33
|
+
// tishlang_cranelift_runtime crate lives in crates/tish_cranelift_runtime
|
|
34
|
+
let runtime_path = workspace_root
|
|
35
|
+
.join("crates")
|
|
36
|
+
.join("tish_cranelift_runtime")
|
|
37
|
+
.canonicalize()
|
|
38
|
+
.map_err(|e| CraneliftError {
|
|
39
|
+
message: format!("Cannot find tishlang_cranelift_runtime: {}", e),
|
|
40
|
+
})?
|
|
41
|
+
.display()
|
|
42
|
+
.to_string()
|
|
43
|
+
.replace('\\', "/");
|
|
44
|
+
|
|
45
|
+
let features_str = if features.is_empty() {
|
|
46
|
+
String::new()
|
|
47
|
+
} else {
|
|
48
|
+
format!(
|
|
49
|
+
", features = [{}]",
|
|
50
|
+
features
|
|
51
|
+
.iter()
|
|
52
|
+
.map(|f| format!("{:?}", f))
|
|
53
|
+
.collect::<Vec<_>>()
|
|
54
|
+
.join(", ")
|
|
55
|
+
)
|
|
56
|
+
};
|
|
57
|
+
let cargo_toml_fixed = format!(
|
|
58
|
+
r#"[package]
|
|
59
|
+
name = "tishlang_cranelift_out"
|
|
60
|
+
version = "0.1.0"
|
|
61
|
+
edition = "2021"
|
|
62
|
+
|
|
63
|
+
[[bin]]
|
|
64
|
+
name = "{}"
|
|
65
|
+
path = "src/main.rs"
|
|
66
|
+
|
|
67
|
+
[dependencies]
|
|
68
|
+
tishlang_cranelift_runtime = {{ path = {:?}{} }}
|
|
69
|
+
"#,
|
|
70
|
+
out_name, runtime_path, features_str
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
let main_rs = r#"
|
|
74
|
+
extern "C" {
|
|
75
|
+
static tish_chunk_data: [u8; 1];
|
|
76
|
+
static tish_chunk_len: u64;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
fn main() {
|
|
80
|
+
let len = unsafe { tish_chunk_len } as usize;
|
|
81
|
+
let ptr = unsafe { tish_chunk_data.as_ptr() };
|
|
82
|
+
let exit_code = tishlang_cranelift_runtime::tish_run_chunk(ptr, len);
|
|
83
|
+
std::process::exit(exit_code);
|
|
84
|
+
}
|
|
85
|
+
"#;
|
|
86
|
+
|
|
87
|
+
let build_rs = format!(
|
|
88
|
+
r#"
|
|
89
|
+
fn main() {{
|
|
90
|
+
println!("cargo:rustc-link-arg={}");
|
|
91
|
+
}}
|
|
92
|
+
"#,
|
|
93
|
+
object_path_str
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
fs::write(build_dir.join("Cargo.toml"), cargo_toml_fixed).map_err(|e| CraneliftError {
|
|
97
|
+
message: format!("Cannot write Cargo.toml: {}", e),
|
|
98
|
+
})?;
|
|
99
|
+
fs::write(build_dir.join("src/main.rs"), main_rs).map_err(|e| CraneliftError {
|
|
100
|
+
message: format!("Cannot write main.rs: {}", e),
|
|
101
|
+
})?;
|
|
102
|
+
fs::write(build_dir.join("build.rs"), build_rs).map_err(|e| CraneliftError {
|
|
103
|
+
message: format!("Cannot write build.rs: {}", e),
|
|
104
|
+
})?;
|
|
105
|
+
|
|
106
|
+
tishlang_build_utils::run_cargo_build(&build_dir, None, None)
|
|
107
|
+
.map_err(|e| CraneliftError { message: e })?;
|
|
108
|
+
|
|
109
|
+
let binary_dir = build_dir.join("target").join("release");
|
|
110
|
+
let binary = tishlang_build_utils::find_release_binary(&binary_dir, out_name)
|
|
111
|
+
.map_err(|e| CraneliftError { message: e })?;
|
|
112
|
+
let target = tishlang_build_utils::resolve_output_path(output_path, out_name);
|
|
113
|
+
tishlang_build_utils::copy_binary_to_output(&binary, &target)
|
|
114
|
+
.map_err(|e| CraneliftError { message: e })?;
|
|
115
|
+
|
|
116
|
+
Ok(())
|
|
117
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
//! Embed serialized bytecode in an object file for the standalone native binary.
|
|
2
|
+
//!
|
|
3
|
+
//! **This is not AOT compilation of Tish into Cranelift IR.** The chunk is stored as
|
|
4
|
+
//! read-only data (`tish_chunk_data`, `tish_chunk_len`). The link step produces an
|
|
5
|
+
//! executable that **deserializes the chunk and runs `tishlang_vm`** — same VM as
|
|
6
|
+
//! `tish run --backend vm`. Cranelift is only the object-file emitter for that blob.
|
|
7
|
+
|
|
8
|
+
use std::path::Path;
|
|
9
|
+
|
|
10
|
+
use cranelift::codegen::settings;
|
|
11
|
+
use cranelift::codegen::settings::Configurable;
|
|
12
|
+
use cranelift_module::{DataDescription, Linkage, Module};
|
|
13
|
+
use cranelift_object::{ObjectBuilder, ObjectModule};
|
|
14
|
+
|
|
15
|
+
use tishlang_bytecode::{serialize, Chunk};
|
|
16
|
+
|
|
17
|
+
use crate::CraneliftError;
|
|
18
|
+
|
|
19
|
+
pub fn lower_and_emit(chunk: &Chunk, object_path: &Path) -> Result<(), CraneliftError> {
|
|
20
|
+
let mut settings_builder = settings::builder();
|
|
21
|
+
settings_builder
|
|
22
|
+
.set("opt_level", "speed")
|
|
23
|
+
.map_err(|_| CraneliftError {
|
|
24
|
+
message: "Failed to set opt_level".to_string(),
|
|
25
|
+
})?;
|
|
26
|
+
let flags = settings::Flags::new(settings_builder);
|
|
27
|
+
|
|
28
|
+
let isa_builder = cranelift_native::builder().map_err(|e| CraneliftError {
|
|
29
|
+
message: format!("Failed to build ISA: {}", e),
|
|
30
|
+
})?;
|
|
31
|
+
let isa = isa_builder.finish(flags).map_err(|e| CraneliftError {
|
|
32
|
+
message: format!("Failed to finish ISA: {}", e),
|
|
33
|
+
})?;
|
|
34
|
+
|
|
35
|
+
let object_builder = ObjectBuilder::new(
|
|
36
|
+
isa,
|
|
37
|
+
"tishlang_cranelift",
|
|
38
|
+
cranelift_module::default_libcall_names(),
|
|
39
|
+
)
|
|
40
|
+
.map_err(|e| CraneliftError {
|
|
41
|
+
message: format!("Failed to create ObjectBuilder: {}", e),
|
|
42
|
+
})?;
|
|
43
|
+
let mut module = ObjectModule::new(object_builder);
|
|
44
|
+
|
|
45
|
+
// Serialize chunk and emit as data - link step will build a Rust binary that reads it
|
|
46
|
+
let chunk_data = serialize(chunk);
|
|
47
|
+
let chunk_len = chunk_data.len() as u64;
|
|
48
|
+
let data_id = module
|
|
49
|
+
.declare_data("tish_chunk_data", Linkage::Export, false, false)
|
|
50
|
+
.map_err(|e| CraneliftError {
|
|
51
|
+
message: format!("Failed to declare chunk data: {}", e),
|
|
52
|
+
})?;
|
|
53
|
+
let mut data_desc = DataDescription::new();
|
|
54
|
+
data_desc.define(chunk_data.into_boxed_slice());
|
|
55
|
+
module
|
|
56
|
+
.define_data(data_id, &data_desc)
|
|
57
|
+
.map_err(|e| CraneliftError {
|
|
58
|
+
message: format!("Failed to define chunk data: {}", e),
|
|
59
|
+
})?;
|
|
60
|
+
|
|
61
|
+
let len_data = chunk_len.to_le_bytes();
|
|
62
|
+
let len_id = module
|
|
63
|
+
.declare_data("tish_chunk_len", Linkage::Export, false, false)
|
|
64
|
+
.map_err(|e| CraneliftError {
|
|
65
|
+
message: format!("Failed to declare chunk len: {}", e),
|
|
66
|
+
})?;
|
|
67
|
+
let mut len_desc = DataDescription::new();
|
|
68
|
+
len_desc.define(len_data.to_vec().into_boxed_slice());
|
|
69
|
+
module
|
|
70
|
+
.define_data(len_id, &len_desc)
|
|
71
|
+
.map_err(|e| CraneliftError {
|
|
72
|
+
message: format!("Failed to define chunk len: {}", e),
|
|
73
|
+
})?;
|
|
74
|
+
|
|
75
|
+
let object_product = module.finish();
|
|
76
|
+
let bytes = object_product.emit().map_err(|e| CraneliftError {
|
|
77
|
+
message: format!("Failed to emit object: {}", e),
|
|
78
|
+
})?;
|
|
79
|
+
|
|
80
|
+
std::fs::write(object_path, bytes).map_err(|e| CraneliftError {
|
|
81
|
+
message: format!("Failed to write object file: {}", e),
|
|
82
|
+
})?;
|
|
83
|
+
|
|
84
|
+
Ok(())
|
|
85
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "tishlang_cranelift_runtime"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
description = "Runtime for Cranelift-compiled Tish bytecode"
|
|
6
|
+
|
|
7
|
+
license-file = { workspace = true }
|
|
8
|
+
repository = { workspace = true }
|
|
9
|
+
[features]
|
|
10
|
+
default = []
|
|
11
|
+
fs = ["tishlang_vm/fs"]
|
|
12
|
+
process = ["tishlang_vm/process"]
|
|
13
|
+
http = ["tishlang_vm/http"]
|
|
14
|
+
promise = ["tishlang_vm/promise"]
|
|
15
|
+
timers = ["tishlang_vm/timers"]
|
|
16
|
+
ws = ["tishlang_vm/ws"]
|
|
17
|
+
regex = ["tishlang_vm/regex"]
|
|
18
|
+
|
|
19
|
+
[lib]
|
|
20
|
+
crate-type = ["staticlib", "rlib"]
|
|
21
|
+
|
|
22
|
+
[dependencies]
|
|
23
|
+
tishlang_bytecode = { path = "../tish_bytecode", version = ">=0.1" }
|
|
24
|
+
tishlang_vm = { path = "../tish_vm", version = ">=0.1" }
|
|
25
|
+
tishlang_core = { path = "../tish_core", version = ">=0.1" }
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//! Runtime linked into the `tish build --native-backend cranelift` executable.
|
|
2
|
+
//!
|
|
3
|
+
//! **`tish_run_chunk`** deserializes embedded bytecode and runs **`tishlang_vm`** — the same
|
|
4
|
+
//! execution engine as `tish run --backend vm`. The crate name is historical; this is not
|
|
5
|
+
//! running CLIF-emitted machine code for each Tish opcode.
|
|
6
|
+
|
|
7
|
+
use tishlang_bytecode::deserialize;
|
|
8
|
+
use tishlang_vm::Vm;
|
|
9
|
+
|
|
10
|
+
/// Serialization format:
|
|
11
|
+
/// - u64: code len
|
|
12
|
+
/// - bytes: code
|
|
13
|
+
/// - u64: constants count
|
|
14
|
+
/// - for each constant: u8 tag + payload
|
|
15
|
+
/// - u64: names count
|
|
16
|
+
/// - for each name: u64 len + bytes
|
|
17
|
+
///
|
|
18
|
+
/// Rust-callable wrapper. Run serialized chunk data. Returns exit code (0 on success).
|
|
19
|
+
pub fn tish_run_chunk(ptr: *const u8, len: usize) -> i32 {
|
|
20
|
+
tish_run_chunk_impl(ptr, len)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#[no_mangle]
|
|
24
|
+
extern "C" fn tish_run_chunk_impl(ptr: *const u8, len: usize) -> i32 {
|
|
25
|
+
if ptr.is_null() || len < 8 {
|
|
26
|
+
return 1;
|
|
27
|
+
}
|
|
28
|
+
let slice = unsafe { std::slice::from_raw_parts(ptr, len) };
|
|
29
|
+
match deserialize(slice) {
|
|
30
|
+
Ok(chunk) => {
|
|
31
|
+
let mut vm = Vm::new();
|
|
32
|
+
match vm.run(&chunk) {
|
|
33
|
+
Ok(_) => 0,
|
|
34
|
+
Err(e) => {
|
|
35
|
+
eprintln!("Runtime error: {}", e);
|
|
36
|
+
1
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
Err(e) => {
|
|
41
|
+
eprintln!("Deserialization error: {}", e);
|
|
42
|
+
1
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "tishlang_eval"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
description = "Tish tree-walk interpreter"
|
|
6
|
+
|
|
7
|
+
license-file = { workspace = true }
|
|
8
|
+
repository = { workspace = true }
|
|
9
|
+
[features]
|
|
10
|
+
default = []
|
|
11
|
+
# setTimeout / setInterval / clear* (standalone or with `import { … } from "tish:timers"`).
|
|
12
|
+
timers = []
|
|
13
|
+
http = [
|
|
14
|
+
"timers",
|
|
15
|
+
"tokio",
|
|
16
|
+
"reqwest",
|
|
17
|
+
"futures",
|
|
18
|
+
"tiny_http",
|
|
19
|
+
"tishlang_core/regex",
|
|
20
|
+
"dep:tishlang_runtime",
|
|
21
|
+
"tishlang_runtime/http",
|
|
22
|
+
# Interpreter + http means the runtime's `NativeFn` is `Arc<... + Send>`,
|
|
23
|
+
# so the interpreter's `CoreFn` variant must use the same shape.
|
|
24
|
+
"tishlang_core/send-values",
|
|
25
|
+
"tishlang_builtins/send-values",
|
|
26
|
+
]
|
|
27
|
+
fs = []
|
|
28
|
+
process = []
|
|
29
|
+
regex = ["dep:fancy-regex", "tishlang_core/regex"]
|
|
30
|
+
tokio = ["dep:tokio"]
|
|
31
|
+
ws = ["dep:tishlang_runtime", "tishlang_runtime/ws"]
|
|
32
|
+
|
|
33
|
+
[dependencies]
|
|
34
|
+
ahash = "0.8.12"
|
|
35
|
+
rand = "0.10.1"
|
|
36
|
+
tishlang_ast = { path = "../tish_ast", version = ">=0.1" }
|
|
37
|
+
tishlang_builtins = { path = "../tish_builtins", version = ">=0.1" }
|
|
38
|
+
tishlang_parser = { path = "../tish_parser", version = ">=0.1" }
|
|
39
|
+
tishlang_core = { path = "../tish_core", version = ">=0.1" }
|
|
40
|
+
reqwest = { version = "0.13.2", default-features = false, features = ["rustls", "json", "stream"], optional = true }
|
|
41
|
+
futures = { version = "0.3.32", optional = true }
|
|
42
|
+
tiny_http = { version = "0.12.0", optional = true }
|
|
43
|
+
fancy-regex = { version = "0.17.0", optional = true }
|
|
44
|
+
tokio = { version = "1.50.0", features = ["rt-multi-thread", "time", "sync"], optional = true }
|
|
45
|
+
tishlang_runtime = { path = "../tish_runtime", version = ">=0.1", optional = true }
|