@tishlang/tish-format 1.0.12 → 2.0.1
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 +51 -0
- package/LICENSE +13 -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 +611 -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 +62 -0
- package/crates/tish/build.rs +21 -0
- package/crates/tish/src/cargo_native_registry.rs +32 -0
- package/crates/tish/src/cli_help.rs +576 -0
- package/crates/tish/src/main.rs +853 -0
- package/crates/tish/src/repl_completion.rs +199 -0
- package/crates/tish/tests/cargo_example_compile.rs +67 -0
- package/crates/tish/tests/error_source_location.rs +36 -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/fixtures/runtime_error_location.tish +5 -0
- package/crates/tish/tests/fixtures/trycatch_runtime_errors.tish +15 -0
- package/crates/tish/tests/fixtures/tty_capability.tish +9 -0
- package/crates/tish/tests/integration_test.rs +1406 -0
- package/crates/tish/tests/run_optimize_stdout_parity.rs +50 -0
- package/crates/tish/tests/shortcircuit.rs +65 -0
- package/crates/tish/tests/trycatch_runtime_errors.rs +45 -0
- package/crates/tish/tests/tty_capability.rs +43 -0
- package/crates/tish_ast/Cargo.toml +9 -0
- package/crates/tish_ast/src/ast.rs +649 -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 +22 -0
- package/crates/tish_builtins/src/array.rs +803 -0
- package/crates/tish_builtins/src/collections.rs +481 -0
- package/crates/tish_builtins/src/construct.rs +199 -0
- package/crates/tish_builtins/src/date.rs +538 -0
- package/crates/tish_builtins/src/globals.rs +293 -0
- package/crates/tish_builtins/src/helpers.rs +35 -0
- package/crates/tish_builtins/src/iterator.rs +129 -0
- package/crates/tish_builtins/src/lib.rs +21 -0
- package/crates/tish_builtins/src/math.rs +89 -0
- package/crates/tish_builtins/src/number.rs +96 -0
- package/crates/tish_builtins/src/object.rs +36 -0
- package/crates/tish_builtins/src/string.rs +646 -0
- package/crates/tish_builtins/src/symbol.rs +83 -0
- package/crates/tish_builtins/src/typedarrays.rs +298 -0
- package/crates/tish_bytecode/Cargo.toml +17 -0
- package/crates/tish_bytecode/src/chunk.rs +164 -0
- package/crates/tish_bytecode/src/compiler.rs +2604 -0
- package/crates/tish_bytecode/src/encoding.rs +102 -0
- package/crates/tish_bytecode/src/lib.rs +20 -0
- package/crates/tish_bytecode/src/opcode.rs +185 -0
- package/crates/tish_bytecode/src/peephole.rs +189 -0
- package/crates/tish_bytecode/src/serialize.rs +193 -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 +27 -0
- package/crates/tish_compile/src/check.rs +774 -0
- package/crates/tish_compile/src/codegen.rs +7317 -0
- package/crates/tish_compile/src/infer.rs +1681 -0
- package/crates/tish_compile/src/lib.rs +206 -0
- package/crates/tish_compile/src/resolve.rs +1951 -0
- package/crates/tish_compile/src/types.rs +605 -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 +938 -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 +414 -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 +32 -0
- package/crates/tish_core/src/console_style.rs +170 -0
- package/crates/tish_core/src/json.rs +430 -0
- package/crates/tish_core/src/lib.rs +20 -0
- package/crates/tish_core/src/macros.rs +36 -0
- package/crates/tish_core/src/shape.rs +85 -0
- package/crates/tish_core/src/uri.rs +118 -0
- package/crates/tish_core/src/value.rs +1350 -0
- package/crates/tish_core/src/vmref.rs +183 -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 +130 -0
- package/crates/tish_cranelift/src/lower.rs +85 -0
- package/crates/tish_cranelift_runtime/Cargo.toml +26 -0
- package/crates/tish_cranelift_runtime/src/lib.rs +45 -0
- package/crates/tish_eval/Cargo.toml +51 -0
- package/crates/tish_eval/src/eval.rs +4265 -0
- package/crates/tish_eval/src/http.rs +191 -0
- package/crates/tish_eval/src/lib.rs +99 -0
- package/crates/tish_eval/src/natives.rs +551 -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 +336 -0
- package/crates/tish_eval/src/value_convert.rs +117 -0
- package/crates/tish_ffi/Cargo.toml +26 -0
- package/crates/tish_ffi/src/lib.rs +518 -0
- package/crates/tish_ffi/tests/fixtures/testmod/Cargo.toml +18 -0
- package/crates/tish_ffi/tests/fixtures/testmod/src/lib.rs +46 -0
- package/crates/tish_ffi/tests/loader.rs +65 -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 +2157 -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 +1104 -0
- package/crates/tish_lexer/src/token.rs +170 -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 +281 -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 +564 -0
- package/crates/tish_lsp/src/main.rs +1459 -0
- package/crates/tish_native/Cargo.toml +16 -0
- package/crates/tish_native/src/build.rs +481 -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 +1046 -0
- package/crates/tish_parser/Cargo.toml +11 -0
- package/crates/tish_parser/src/lib.rs +386 -0
- package/crates/tish_parser/src/parser.rs +2726 -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 +3601 -0
- package/crates/tish_resolve/src/pos.rs +141 -0
- package/crates/tish_runtime/Cargo.toml +100 -0
- package/crates/tish_runtime/src/http.rs +1347 -0
- package/crates/tish_runtime/src/http_fetch.rs +492 -0
- package/crates/tish_runtime/src/http_hyper.rs +441 -0
- package/crates/tish_runtime/src/http_prefork.rs +189 -0
- package/crates/tish_runtime/src/lib.rs +1447 -0
- package/crates/tish_runtime/src/native_promise.rs +15 -0
- package/crates/tish_runtime/src/promise.rs +558 -0
- package/crates/tish_runtime/src/promise_io.rs +38 -0
- package/crates/tish_runtime/src/timers.rs +172 -0
- package/crates/tish_runtime/src/tty.rs +226 -0
- package/crates/tish_runtime/src/ws.rs +778 -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 +692 -0
- package/crates/tish_ui/src/lib.rs +20 -0
- package/crates/tish_ui/src/runtime/hooks.rs +573 -0
- package/crates/tish_ui/src/runtime/mod.rs +183 -0
- package/crates/tish_vm/Cargo.toml +60 -0
- package/crates/tish_vm/src/jit.rs +1050 -0
- package/crates/tish_vm/src/lib.rs +41 -0
- package/crates/tish_vm/src/vm.rs +3536 -0
- package/crates/tish_vm/tests/concurrent_shared_state.rs +140 -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 +428 -0
- package/crates/tish_wasm_runtime/Cargo.toml +37 -0
- package/crates/tish_wasm_runtime/src/gpu.rs +429 -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 +261 -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 +276 -0
- package/package.json +2 -2
- package/platform/darwin-arm64/tish-fmt +0 -0
- package/platform/darwin-x64/tish-fmt +0 -0
- package/platform/linux-arm64/tish-fmt +0 -0
- package/platform/linux-x64/tish-fmt +0 -0
- package/platform/win32-x64/tish-fmt.exe +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
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 parking_lot::Mutex;
|
|
114
|
+
use std::sync::Arc;
|
|
115
|
+
|
|
116
|
+
#[derive(Default)]
|
|
117
|
+
pub struct VmRef<T: ?Sized>(pub(super) Arc<Mutex<T>>);
|
|
118
|
+
|
|
119
|
+
/// Read guard alias. On the multi-threaded path both readers and
|
|
120
|
+
/// writers share a single `MutexGuard` (exclusive access).
|
|
121
|
+
pub type ReadGuard<'a, T> = parking_lot::MutexGuard<'a, T>;
|
|
122
|
+
/// Write guard alias.
|
|
123
|
+
pub type WriteGuard<'a, T> = parking_lot::MutexGuard<'a, T>;
|
|
124
|
+
|
|
125
|
+
impl<T> VmRef<T> {
|
|
126
|
+
#[inline]
|
|
127
|
+
pub fn new(value: T) -> Self {
|
|
128
|
+
VmRef(Arc::new(Mutex::new(value)))
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
impl<T: ?Sized> VmRef<T> {
|
|
133
|
+
/// Acquire the inner mutex. `parking_lot::Mutex` is used (not
|
|
134
|
+
/// `std::sync::Mutex`): its uncontended lock is a single atomic with
|
|
135
|
+
/// no pthread syscall — a profile of object/array-heavy code showed
|
|
136
|
+
/// `pthread_mutex_lock/unlock` as a top cost under send-values, since
|
|
137
|
+
/// every property/element access locks. It also has no poisoning, so
|
|
138
|
+
/// there is no `Result` to swallow (a handler panic aborts the thread
|
|
139
|
+
/// regardless).
|
|
140
|
+
#[inline]
|
|
141
|
+
pub fn borrow(&self) -> ReadGuard<'_, T> {
|
|
142
|
+
self.0.lock()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
#[inline]
|
|
146
|
+
pub fn borrow_mut(&self) -> WriteGuard<'_, T> {
|
|
147
|
+
self.0.lock()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
#[inline]
|
|
151
|
+
pub fn ptr_eq(a: &Self, b: &Self) -> bool {
|
|
152
|
+
Arc::ptr_eq(&a.0, &b.0)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
#[inline]
|
|
156
|
+
pub fn strong_count(this: &Self) -> usize {
|
|
157
|
+
Arc::strong_count(&this.0)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
impl<T: ?Sized> Clone for VmRef<T> {
|
|
162
|
+
#[inline]
|
|
163
|
+
fn clone(&self) -> Self {
|
|
164
|
+
VmRef(Arc::clone(&self.0))
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
pub use imp::{ReadGuard as VmReadGuard, VmRef, WriteGuard as VmWriteGuard};
|
|
170
|
+
|
|
171
|
+
impl<T: fmt::Debug> fmt::Debug for VmRef<T> {
|
|
172
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
173
|
+
// Match `RefCell`'s debug format so snapshot-test output stays
|
|
174
|
+
// stable across the migration.
|
|
175
|
+
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
|
176
|
+
let guard = self.borrow();
|
|
177
|
+
format!("{:?}", &*guard)
|
|
178
|
+
})) {
|
|
179
|
+
Ok(s) => write!(f, "RefCell {{ value: {} }}", s),
|
|
180
|
+
Err(_) => write!(f, "RefCell {{ value: <borrowed> }}"),
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -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,130 @@
|
|
|
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
|
+
// UNIQUE package name per program. With the shared target dir, a fixed package name
|
|
58
|
+
// (`tishlang_cranelift_out`) made cargo cross-contaminate builds — each program links its own
|
|
59
|
+
// per-program object via build.rs, so program B would reuse program A's cached build script and
|
|
60
|
+
// link A's (now-deleted) `.o`. A distinct package name keeps them separate while the heavy
|
|
61
|
+
// dependency (`tishlang_cranelift_runtime`, same path + features for all) still compiles once.
|
|
62
|
+
let pkg_name = format!("clout_{}", out_name);
|
|
63
|
+
let cargo_toml_fixed = format!(
|
|
64
|
+
r#"[package]
|
|
65
|
+
name = "{}"
|
|
66
|
+
version = "0.1.0"
|
|
67
|
+
edition = "2021"
|
|
68
|
+
|
|
69
|
+
[[bin]]
|
|
70
|
+
name = "{}"
|
|
71
|
+
path = "src/main.rs"
|
|
72
|
+
|
|
73
|
+
[dependencies]
|
|
74
|
+
tishlang_cranelift_runtime = {{ path = {:?}{} }}
|
|
75
|
+
"#,
|
|
76
|
+
pkg_name, out_name, runtime_path, features_str
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
let main_rs = r#"
|
|
80
|
+
extern "C" {
|
|
81
|
+
static tish_chunk_data: [u8; 1];
|
|
82
|
+
static tish_chunk_len: u64;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
fn main() {
|
|
86
|
+
let len = unsafe { tish_chunk_len } as usize;
|
|
87
|
+
let ptr = unsafe { tish_chunk_data.as_ptr() };
|
|
88
|
+
let exit_code = tishlang_cranelift_runtime::tish_run_chunk(ptr, len);
|
|
89
|
+
std::process::exit(exit_code);
|
|
90
|
+
}
|
|
91
|
+
"#;
|
|
92
|
+
|
|
93
|
+
let build_rs = format!(
|
|
94
|
+
r#"
|
|
95
|
+
fn main() {{
|
|
96
|
+
println!("cargo:rustc-link-arg={}");
|
|
97
|
+
}}
|
|
98
|
+
"#,
|
|
99
|
+
object_path_str
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
fs::write(build_dir.join("Cargo.toml"), cargo_toml_fixed).map_err(|e| CraneliftError {
|
|
103
|
+
message: format!("Cannot write Cargo.toml: {}", e),
|
|
104
|
+
})?;
|
|
105
|
+
fs::write(build_dir.join("src/main.rs"), main_rs).map_err(|e| CraneliftError {
|
|
106
|
+
message: format!("Cannot write main.rs: {}", e),
|
|
107
|
+
})?;
|
|
108
|
+
fs::write(build_dir.join("build.rs"), build_rs).map_err(|e| CraneliftError {
|
|
109
|
+
message: format!("Cannot write build.rs: {}", e),
|
|
110
|
+
})?;
|
|
111
|
+
|
|
112
|
+
// Build into a SHARED target dir (one per host), not the per-program `build_dir/target`.
|
|
113
|
+
// The heavy deps (cranelift_codegen + the embedded VM, ~several GB) then compile ONCE and
|
|
114
|
+
// are reused by every cranelift build; only each program's tiny main + object is rebuilt.
|
|
115
|
+
// Without this, every program left its own multi-GB `target/` behind and a full-suite sweep
|
|
116
|
+
// filled the disk (see docs/full-backend-parity-plan.md A3). Concurrent builds are serialized
|
|
117
|
+
// by `run_cargo_build`'s nested-cargo mutex and cargo's own target lock, so sharing is safe.
|
|
118
|
+
let shared_target = std::env::temp_dir().join("tishlang_cranelift_target");
|
|
119
|
+
tishlang_build_utils::run_cargo_build(&build_dir, Some(&shared_target), None)
|
|
120
|
+
.map_err(|e| CraneliftError { message: e })?;
|
|
121
|
+
|
|
122
|
+
let binary_dir = shared_target.join("release");
|
|
123
|
+
let binary = tishlang_build_utils::find_release_binary(&binary_dir, out_name)
|
|
124
|
+
.map_err(|e| CraneliftError { message: e })?;
|
|
125
|
+
let target = tishlang_build_utils::resolve_output_path(output_path, out_name);
|
|
126
|
+
tishlang_build_utils::copy_binary_to_output(&binary, &target)
|
|
127
|
+
.map_err(|e| CraneliftError { message: e })?;
|
|
128
|
+
|
|
129
|
+
Ok(())
|
|
130
|
+
}
|
|
@@ -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,26 @@
|
|
|
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
|
+
tty = ["tishlang_vm/tty"]
|
|
18
|
+
regex = ["tishlang_vm/regex"]
|
|
19
|
+
|
|
20
|
+
[lib]
|
|
21
|
+
crate-type = ["staticlib", "rlib"]
|
|
22
|
+
|
|
23
|
+
[dependencies]
|
|
24
|
+
tishlang_bytecode = { path = "../tish_bytecode", version = ">=0.1" }
|
|
25
|
+
tishlang_vm = { path = "../tish_vm", version = ">=0.1" }
|
|
26
|
+
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,51 @@
|
|
|
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
|
+
tty = ["dep:tishlang_runtime", "tishlang_runtime/tty"]
|
|
30
|
+
regex = ["dep:fancy-regex", "tishlang_core/regex"]
|
|
31
|
+
tokio = ["dep:tokio"]
|
|
32
|
+
ws = ["dep:tishlang_runtime", "tishlang_runtime/ws"]
|
|
33
|
+
|
|
34
|
+
[dependencies]
|
|
35
|
+
ahash = "0.8.12"
|
|
36
|
+
indexmap = "2"
|
|
37
|
+
rand = "0.10.1"
|
|
38
|
+
# On-demand native stack growth for deep (non-tail) recursion in the tree-walker,
|
|
39
|
+
# mirroring the bytecode VM. Plain dep (stacker is a no-op shim on wasm32, where
|
|
40
|
+
# tish_vm already depends on it for the wasi backend).
|
|
41
|
+
stacker = "0.1"
|
|
42
|
+
tishlang_ast = { path = "../tish_ast", version = ">=0.1" }
|
|
43
|
+
tishlang_builtins = { path = "../tish_builtins", version = ">=0.1" }
|
|
44
|
+
tishlang_parser = { path = "../tish_parser", version = ">=0.1" }
|
|
45
|
+
tishlang_core = { path = "../tish_core", version = ">=0.1" }
|
|
46
|
+
reqwest = { version = "0.13.2", default-features = false, features = ["rustls", "json", "stream"], optional = true }
|
|
47
|
+
futures = { version = "0.3.32", optional = true }
|
|
48
|
+
tiny_http = { version = "0.12.0", optional = true }
|
|
49
|
+
fancy-regex = { version = "0.17.0", optional = true }
|
|
50
|
+
tokio = { version = "1.50.0", features = ["rt-multi-thread", "time", "sync"], optional = true }
|
|
51
|
+
tishlang_runtime = { path = "../tish_runtime", version = ">=0.1", optional = true }
|