@tishlang/tish 1.7.0 → 1.8.0
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 +1 -0
- package/README.md +2 -0
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/transform/expr.rs +28 -8
- package/crates/js_to_tish/src/transform/stmt.rs +49 -22
- package/crates/tish/Cargo.toml +15 -5
- package/crates/tish/src/cargo_native_registry.rs +29 -0
- package/crates/tish/src/cli_help.rs +16 -10
- package/crates/tish/src/main.rs +87 -32
- package/crates/tish/src/repl_completion.rs +3 -3
- package/crates/tish/tests/cargo_example_compile.rs +1 -1
- package/crates/tish/tests/integration_test.rs +19 -7
- package/crates/tish/tests/shortcircuit.rs +1 -1
- package/crates/tish_ast/src/ast.rs +80 -9
- package/crates/tish_build_utils/Cargo.toml +4 -0
- package/crates/tish_build_utils/src/lib.rs +105 -2
- package/crates/tish_builtins/Cargo.toml +5 -1
- package/crates/tish_builtins/src/array.rs +13 -12
- package/crates/tish_builtins/src/construct.rs +34 -33
- package/crates/tish_builtins/src/globals.rs +12 -11
- package/crates/tish_builtins/src/helpers.rs +2 -1
- package/crates/tish_builtins/src/object.rs +3 -2
- package/crates/tish_builtins/src/string.rs +73 -3
- package/crates/tish_bytecode/src/compiler.rs +12 -14
- package/crates/tish_bytecode/src/opcode.rs +12 -3
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +745 -199
- package/crates/tish_compile/src/infer.rs +6 -0
- package/crates/tish_compile/src/lib.rs +4 -3
- package/crates/tish_compile/src/resolve.rs +180 -82
- package/crates/tish_compile/src/types.rs +175 -11
- package/crates/tish_compile_js/Cargo.toml +1 -0
- package/crates/tish_compile_js/src/codegen.rs +152 -29
- package/crates/tish_compile_js/src/lib.rs +3 -1
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +31 -12
- package/crates/tish_core/Cargo.toml +8 -0
- package/crates/tish_core/src/json.rs +102 -53
- package/crates/tish_core/src/lib.rs +3 -1
- package/crates/tish_core/src/macros.rs +5 -5
- package/crates/tish_core/src/value.rs +53 -15
- package/crates/tish_core/src/vmref.rs +178 -0
- package/crates/tish_eval/Cargo.toml +17 -2
- package/crates/tish_eval/src/eval.rs +90 -28
- package/crates/tish_eval/src/http.rs +61 -0
- package/crates/tish_eval/src/lib.rs +3 -3
- package/crates/tish_eval/src/natives.rs +41 -0
- package/crates/tish_eval/src/value.rs +7 -3
- package/crates/tish_eval/src/value_convert.rs +13 -5
- package/crates/tish_fmt/src/lib.rs +120 -30
- package/crates/tish_lexer/src/lib.rs +20 -5
- package/crates/tish_lexer/src/token.rs +4 -0
- package/crates/tish_llvm/src/lib.rs +3 -1
- package/crates/tish_lsp/Cargo.toml +4 -1
- package/crates/tish_lsp/README.md +1 -1
- package/crates/tish_lsp/src/builtin_goto.rs +261 -0
- package/crates/tish_lsp/src/import_goto.rs +549 -0
- package/crates/tish_lsp/src/main.rs +502 -102
- package/crates/tish_native/src/build.rs +3 -2
- package/crates/tish_native/src/lib.rs +6 -2
- package/crates/tish_opt/src/lib.rs +17 -2
- package/crates/tish_parser/src/lib.rs +10 -3
- package/crates/tish_parser/src/parser.rs +346 -56
- package/crates/tish_resolve/Cargo.toml +13 -0
- package/crates/tish_resolve/src/lib.rs +3436 -0
- package/crates/tish_resolve/src/pos.rs +133 -0
- package/crates/tish_runtime/Cargo.toml +68 -3
- package/crates/tish_runtime/src/http.rs +1123 -141
- package/crates/tish_runtime/src/http_fetch.rs +15 -14
- 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 +159 -29
- package/crates/tish_runtime/src/promise.rs +199 -36
- package/crates/tish_runtime/src/promise_io.rs +2 -1
- package/crates/tish_runtime/src/timers.rs +37 -1
- package/crates/tish_runtime/src/ws.rs +26 -28
- package/crates/tish_ui/src/jsx.rs +279 -8
- package/crates/tish_ui/src/lib.rs +5 -2
- package/crates/tish_ui/src/runtime/hooks.rs +406 -45
- package/crates/tish_ui/src/runtime/mod.rs +36 -9
- package/crates/tish_vm/Cargo.toml +15 -5
- package/crates/tish_vm/src/vm.rs +506 -259
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +3 -1
- package/crates/tish_wasm/src/lib.rs +17 -14
- package/crates/tish_wasm_runtime/Cargo.toml +2 -1
- package/crates/tish_wasm_runtime/src/lib.rs +1 -1
- package/crates/tishlang_cargo_bindgen/Cargo.toml +1 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +68 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +5 -4
- package/justfile +8 -0
- package/package.json +1 -1
- package/platform/darwin-arm64/tish +0 -0
- package/platform/darwin-x64/tish +0 -0
- package/platform/linux-arm64/tish +0 -0
- package/platform/linux-x64/tish +0 -0
- package/platform/win32-x64/tish.exe +0 -0
|
@@ -1,72 +1,235 @@
|
|
|
1
|
-
//! Promise
|
|
1
|
+
//! Promise helpers for the bytecode VM and native codegen (`Promise.resolve`, etc.).
|
|
2
|
+
//!
|
|
3
|
+
//! The global `Promise` value is an **object** with a `__call` entry so the VM can
|
|
4
|
+
//! invoke `Promise(executor)` like `new Promise(executor)` in JS. Static methods live
|
|
5
|
+
//! on the same object (`resolve`, `reject`, `all`, `race`).
|
|
2
6
|
|
|
3
|
-
use std::
|
|
4
|
-
use std::
|
|
5
|
-
use std::sync::Arc;
|
|
6
|
-
use tishlang_core::{ObjectMap, Value};
|
|
7
|
+
use std::sync::mpsc;
|
|
8
|
+
use std::sync::{Arc, Mutex};
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
use tishlang_core::{ObjectMap, TishPromise, Value, VmRef};
|
|
11
|
+
|
|
12
|
+
/// Fulfilled or rejected before anyone awaits — `block_until_settled` consumes the result once.
|
|
13
|
+
pub struct ImmediateSettledPromise {
|
|
14
|
+
slot: Mutex<Option<Result<Value, Value>>>,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
impl ImmediateSettledPromise {
|
|
18
|
+
fn new(result: Result<Value, Value>) -> Self {
|
|
19
|
+
Self {
|
|
20
|
+
slot: Mutex::new(Some(result)),
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
impl TishPromise for ImmediateSettledPromise {
|
|
26
|
+
fn block_until_settled(&self) -> std::result::Result<Value, Value> {
|
|
27
|
+
self.slot
|
|
28
|
+
.lock()
|
|
29
|
+
.unwrap()
|
|
30
|
+
.take()
|
|
31
|
+
.unwrap_or(Err(Value::String(
|
|
32
|
+
"Promise already settled or consumed".into(),
|
|
33
|
+
)))
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
fn fulfilled(v: Value) -> Value {
|
|
38
|
+
Value::Promise(Arc::new(ImmediateSettledPromise::new(Ok(v))))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
fn rejected(v: Value) -> Value {
|
|
42
|
+
Value::Promise(Arc::new(ImmediateSettledPromise::new(Err(v))))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fn flatten_chain_out(v: Value) -> std::result::Result<Value, Value> {
|
|
46
|
+
match v {
|
|
47
|
+
Value::Promise(p) => p.block_until_settled(),
|
|
48
|
+
other => Ok(other),
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// `Promise(executor)` — executor runs synchronously; `resolve` / `reject` unblock `recv`.
|
|
53
|
+
struct DeferredChannelPromise {
|
|
54
|
+
rx: Mutex<Option<mpsc::Receiver<Result<Value, Value>>>>,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
impl TishPromise for DeferredChannelPromise {
|
|
58
|
+
fn block_until_settled(&self) -> std::result::Result<Value, Value> {
|
|
59
|
+
let rx = self.rx.lock().unwrap().take();
|
|
60
|
+
match rx {
|
|
61
|
+
Some(r) => r.recv().unwrap_or(Err(Value::String(
|
|
62
|
+
"Promise executor did not call resolve or reject".into(),
|
|
63
|
+
))),
|
|
64
|
+
None => Err(Value::String(
|
|
65
|
+
"Promise already consumed or settled".into(),
|
|
66
|
+
)),
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/// `.then` / `.catch` chain: when awaited, settle the predecessor then optionally invoke a handler.
|
|
72
|
+
pub struct ThenPromise {
|
|
73
|
+
pred: Arc<dyn TishPromise>,
|
|
74
|
+
on_fulfilled: Option<Value>,
|
|
75
|
+
on_rejected: Option<Value>,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
impl TishPromise for ThenPromise {
|
|
79
|
+
fn block_until_settled(&self) -> std::result::Result<Value, Value> {
|
|
80
|
+
match self.pred.block_until_settled() {
|
|
81
|
+
Ok(v) => {
|
|
82
|
+
if let Some(Value::Function(f)) = &self.on_fulfilled {
|
|
83
|
+
flatten_chain_out(f(&[v]))
|
|
84
|
+
} else {
|
|
85
|
+
Ok(v)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
Err(e) => {
|
|
89
|
+
if let Some(Value::Function(f)) = &self.on_rejected {
|
|
90
|
+
flatten_chain_out(f(&[e]))
|
|
91
|
+
} else {
|
|
92
|
+
Err(e)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// `Promise.resolve(value)` — adopt promises, otherwise wrap in a fulfilled promise.
|
|
9
100
|
pub fn promise_resolve(args: &[Value]) -> Value {
|
|
10
|
-
args.first()
|
|
101
|
+
match args.first() {
|
|
102
|
+
Some(Value::Promise(p)) => Value::Promise(Arc::clone(p)),
|
|
103
|
+
Some(v) => fulfilled(v.clone()),
|
|
104
|
+
None => fulfilled(Value::Null),
|
|
105
|
+
}
|
|
11
106
|
}
|
|
12
107
|
|
|
13
|
-
/// Promise.reject(
|
|
14
|
-
/// Note: await on this in native compile may not throw; use try/catch in interpreter for full support.
|
|
108
|
+
/// `Promise.reject(reason)` — always a rejected promise.
|
|
15
109
|
pub fn promise_reject(args: &[Value]) -> Value {
|
|
16
|
-
|
|
110
|
+
rejected(
|
|
111
|
+
args.first()
|
|
112
|
+
.cloned()
|
|
113
|
+
.unwrap_or(Value::Null),
|
|
114
|
+
)
|
|
17
115
|
}
|
|
18
116
|
|
|
19
|
-
/// Promise.all(iterable)
|
|
117
|
+
/// `Promise.all(iterable)` — block on each promise in order; non-promises pass through.
|
|
20
118
|
pub fn promise_all(args: &[Value]) -> Value {
|
|
21
119
|
match args.first() {
|
|
22
120
|
Some(Value::Array(arr)) => {
|
|
23
|
-
let
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Ok(val) => val,
|
|
30
|
-
Err(rejection) => rejection,
|
|
31
|
-
}
|
|
32
|
-
} else {
|
|
33
|
-
v.clone()
|
|
121
|
+
let mut out: Vec<Value> = Vec::new();
|
|
122
|
+
for v in arr.borrow().iter() {
|
|
123
|
+
let item = if let Value::Promise(p) = v {
|
|
124
|
+
match p.block_until_settled() {
|
|
125
|
+
Ok(x) => x,
|
|
126
|
+
Err(rej) => return rejected(rej),
|
|
34
127
|
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
128
|
+
} else {
|
|
129
|
+
v.clone()
|
|
130
|
+
};
|
|
131
|
+
out.push(item);
|
|
132
|
+
}
|
|
133
|
+
fulfilled(Value::Array(VmRef::new(out)))
|
|
38
134
|
}
|
|
39
|
-
Some(v) => v.clone(),
|
|
40
|
-
None => Value::Null,
|
|
135
|
+
Some(v) => fulfilled(v.clone()),
|
|
136
|
+
None => fulfilled(Value::Null),
|
|
41
137
|
}
|
|
42
138
|
}
|
|
43
139
|
|
|
44
|
-
/// Promise.race(iterable)
|
|
140
|
+
/// `Promise.race(iterable)` — first element wins (blocking first promise if it is one).
|
|
45
141
|
pub fn promise_race(args: &[Value]) -> Value {
|
|
46
142
|
match args.first() {
|
|
47
|
-
Some(Value::Array(arr)) =>
|
|
48
|
-
|
|
143
|
+
Some(Value::Array(arr)) => {
|
|
144
|
+
let borrowed = arr.borrow();
|
|
145
|
+
for v in borrowed.iter() {
|
|
146
|
+
if let Value::Promise(p) = v {
|
|
147
|
+
return match p.block_until_settled() {
|
|
148
|
+
Ok(x) => fulfilled(x),
|
|
149
|
+
Err(e) => rejected(e),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return fulfilled(v.clone());
|
|
153
|
+
}
|
|
154
|
+
Value::Null
|
|
155
|
+
}
|
|
156
|
+
Some(v) => fulfilled(v.clone()),
|
|
157
|
+
None => Value::Null,
|
|
49
158
|
}
|
|
50
159
|
}
|
|
51
160
|
|
|
52
|
-
/// Build the Promise object
|
|
161
|
+
/// Build the global `Promise` object: `__call` (constructor) + static methods.
|
|
53
162
|
pub fn promise_object() -> Value {
|
|
54
163
|
let mut map: ObjectMap = ObjectMap::default();
|
|
164
|
+
|
|
165
|
+
let ctor = Value::native(|args: &[Value]| match args.first() {
|
|
166
|
+
Some(Value::Function(f)) => {
|
|
167
|
+
let (tx, rx) = mpsc::channel();
|
|
168
|
+
let tx_cell = Arc::new(Mutex::new(Some(tx)));
|
|
169
|
+
let resolve = Value::native({
|
|
170
|
+
let tx_cell = Arc::clone(&tx_cell);
|
|
171
|
+
move |a: &[Value]| {
|
|
172
|
+
if let Some(t) = tx_cell.lock().unwrap().take() {
|
|
173
|
+
let _ = t.send(Ok(
|
|
174
|
+
a.first().cloned().unwrap_or(Value::Null),
|
|
175
|
+
));
|
|
176
|
+
}
|
|
177
|
+
Value::Null
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
let reject = Value::native({
|
|
181
|
+
let tx_cell = Arc::clone(&tx_cell);
|
|
182
|
+
move |a: &[Value]| {
|
|
183
|
+
if let Some(t) = tx_cell.lock().unwrap().take() {
|
|
184
|
+
let _ = t.send(Err(
|
|
185
|
+
a.first().cloned().unwrap_or(Value::Null),
|
|
186
|
+
));
|
|
187
|
+
}
|
|
188
|
+
Value::Null
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
let _ = f(&[resolve, reject]);
|
|
192
|
+
Value::Promise(Arc::new(DeferredChannelPromise {
|
|
193
|
+
rx: Mutex::new(Some(rx)),
|
|
194
|
+
}))
|
|
195
|
+
}
|
|
196
|
+
_ => Value::Null,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
map.insert(Arc::from("__call"), ctor);
|
|
55
200
|
map.insert(
|
|
56
201
|
Arc::from("resolve"),
|
|
57
|
-
Value::
|
|
202
|
+
Value::native(|args: &[Value]| promise_resolve(args)),
|
|
58
203
|
);
|
|
59
204
|
map.insert(
|
|
60
205
|
Arc::from("reject"),
|
|
61
|
-
Value::
|
|
206
|
+
Value::native(|args: &[Value]| promise_reject(args)),
|
|
62
207
|
);
|
|
63
208
|
map.insert(
|
|
64
209
|
Arc::from("all"),
|
|
65
|
-
Value::
|
|
210
|
+
Value::native(|args: &[Value]| promise_all(args)),
|
|
66
211
|
);
|
|
67
212
|
map.insert(
|
|
68
213
|
Arc::from("race"),
|
|
69
|
-
Value::
|
|
214
|
+
Value::native(|args: &[Value]| promise_race(args)),
|
|
70
215
|
);
|
|
71
|
-
Value::Object(
|
|
216
|
+
Value::Object(VmRef::new(map))
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/// `.then(onFulfilled, onRejected)` for a `Value::Promise` instance (VM `GetMember`).
|
|
220
|
+
pub fn promise_instance_then(p: &Arc<dyn TishPromise>, args: &[Value]) -> Value {
|
|
221
|
+
Value::Promise(Arc::new(ThenPromise {
|
|
222
|
+
pred: Arc::clone(p),
|
|
223
|
+
on_fulfilled: args.first().cloned(),
|
|
224
|
+
on_rejected: args.get(1).cloned(),
|
|
225
|
+
}))
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/// `.catch(onRejected)` for a `Value::Promise` instance.
|
|
229
|
+
pub fn promise_instance_catch(p: &Arc<dyn TishPromise>, args: &[Value]) -> Value {
|
|
230
|
+
Value::Promise(Arc::new(ThenPromise {
|
|
231
|
+
pred: Arc::clone(p),
|
|
232
|
+
on_fulfilled: None,
|
|
233
|
+
on_rejected: args.first().cloned(),
|
|
234
|
+
}))
|
|
72
235
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
//! Promises carrying only Send payloads (string results for text(), etc.).
|
|
2
2
|
|
|
3
3
|
use std::cell::RefCell;
|
|
4
|
+
use tishlang_core::VmRef;
|
|
4
5
|
use std::rc::Rc;
|
|
5
6
|
use std::sync::{Arc, Mutex};
|
|
6
7
|
use tishlang_core::{ObjectMap, TishPromise, Value};
|
|
@@ -10,7 +11,7 @@ fn error_value(msg: String) -> Value {
|
|
|
10
11
|
let mut obj: ObjectMap = ObjectMap::with_capacity(2);
|
|
11
12
|
obj.insert(Arc::from("error"), Value::String(msg.into()));
|
|
12
13
|
obj.insert(Arc::from("ok"), Value::Bool(false));
|
|
13
|
-
Value::Object(
|
|
14
|
+
Value::Object(VmRef::new(obj))
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
pub struct StringResultPromise {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! setTimeout, clearTimeout for compiled Tish and VM.
|
|
1
|
+
//! setTimeout, setInterval, clearTimeout, clearInterval for compiled Tish and VM.
|
|
2
2
|
//! Callbacks run when blocking ops (e.g. ws.receiveTimeout) yield in their poll loop.
|
|
3
3
|
|
|
4
4
|
use std::cell::RefCell;
|
|
@@ -35,11 +35,18 @@ fn extract_num(v: Option<&Value>) -> u64 {
|
|
|
35
35
|
|
|
36
36
|
/// Sleep for ms, running due timers before sleeping. Use this instead of thread::sleep
|
|
37
37
|
/// in blocking loops so setTimeout callbacks can fire.
|
|
38
|
+
#[allow(dead_code)] // Used by embedders with blocking poll loops; AppKit uses [`drain_timers`] instead.
|
|
38
39
|
pub fn sleep_with_drain(ms: u64) {
|
|
39
40
|
run_due_timers();
|
|
40
41
|
std::thread::sleep(Duration::from_millis(ms));
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
/// Run all due timer callbacks (e.g. from an AppKit / GUI event pump).
|
|
45
|
+
#[inline]
|
|
46
|
+
pub fn drain_timers() {
|
|
47
|
+
run_due_timers();
|
|
48
|
+
}
|
|
49
|
+
|
|
43
50
|
/// Run all due timer callbacks.
|
|
44
51
|
fn run_due_timers() {
|
|
45
52
|
let due = take_due_timers();
|
|
@@ -109,6 +116,30 @@ pub fn set_timeout(args: &[Value]) -> Value {
|
|
|
109
116
|
Value::Number(id as f64)
|
|
110
117
|
}
|
|
111
118
|
|
|
119
|
+
/// setInterval(callback, intervalMs, ...args) — first run after `intervalMs`, then repeats.
|
|
120
|
+
pub fn set_interval(args: &[Value]) -> Value {
|
|
121
|
+
let callback = args.first().cloned().unwrap_or(Value::Null);
|
|
122
|
+
let interval_ms = extract_num(args.get(1)).min(3600_000);
|
|
123
|
+
let extra_args: Vec<Value> = args.iter().skip(2).cloned().collect();
|
|
124
|
+
if matches!(callback, Value::Null) {
|
|
125
|
+
return Value::Number(next_id() as f64);
|
|
126
|
+
}
|
|
127
|
+
let id = next_id();
|
|
128
|
+
let due = Instant::now() + Duration::from_millis(interval_ms);
|
|
129
|
+
REGISTRY.with(|r| {
|
|
130
|
+
r.borrow_mut().insert(
|
|
131
|
+
id,
|
|
132
|
+
TimerEntry {
|
|
133
|
+
due,
|
|
134
|
+
callback,
|
|
135
|
+
args: extra_args,
|
|
136
|
+
interval_ms,
|
|
137
|
+
},
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
Value::Number(id as f64)
|
|
141
|
+
}
|
|
142
|
+
|
|
112
143
|
/// clearTimeout(id) - removes timer.
|
|
113
144
|
pub fn clear_timeout(args: &[Value]) -> Value {
|
|
114
145
|
let id = args
|
|
@@ -123,3 +154,8 @@ pub fn clear_timeout(args: &[Value]) -> Value {
|
|
|
123
154
|
});
|
|
124
155
|
Value::Null
|
|
125
156
|
}
|
|
157
|
+
|
|
158
|
+
/// clearInterval(id) — same registry as clearTimeout.
|
|
159
|
+
pub fn clear_interval(args: &[Value]) -> Value {
|
|
160
|
+
clear_timeout(args)
|
|
161
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
//! - **Broadcast** (Node pattern): `server.clients.forEach(ws => ws.send(data))` or iterate room conns and `wsSend(ws, data)` (same as `ws.send(data)`)
|
|
7
7
|
|
|
8
8
|
use std::cell::RefCell;
|
|
9
|
+
use tishlang_core::VmRef;
|
|
9
10
|
use std::collections::HashMap;
|
|
10
11
|
use std::rc::Rc;
|
|
11
12
|
use std::sync::atomic::{AtomicU32, Ordering};
|
|
@@ -199,36 +200,36 @@ fn conn_object(id: u32) -> Value {
|
|
|
199
200
|
obj.insert(Arc::from("readyState"), Value::Number(1.0)); // OPEN
|
|
200
201
|
obj.insert(
|
|
201
202
|
Arc::from("send"),
|
|
202
|
-
Value::
|
|
203
|
+
Value::native(move |args: &[Value]| {
|
|
203
204
|
let data = args
|
|
204
205
|
.first()
|
|
205
206
|
.map(|v| v.to_display_string())
|
|
206
207
|
.unwrap_or_default();
|
|
207
208
|
Value::Bool(conn_send(id, data))
|
|
208
|
-
})
|
|
209
|
+
}),
|
|
209
210
|
);
|
|
210
211
|
obj.insert(
|
|
211
212
|
Arc::from("close"),
|
|
212
|
-
Value::
|
|
213
|
+
Value::native(move |_args: &[Value]| {
|
|
213
214
|
unregister(id);
|
|
214
215
|
Value::Null
|
|
215
|
-
})
|
|
216
|
+
}),
|
|
216
217
|
);
|
|
217
218
|
obj.insert(
|
|
218
219
|
Arc::from("receive"),
|
|
219
|
-
Value::
|
|
220
|
+
Value::native(move |_args: &[Value]| match conn_receive(id) {
|
|
220
221
|
Some(s) => {
|
|
221
222
|
let mut ev: ObjectMap = ObjectMap::default();
|
|
222
223
|
ev.insert(Arc::from("data"), Value::String(s.into()));
|
|
223
|
-
Value::Object(
|
|
224
|
+
Value::Object(VmRef::new(ev))
|
|
224
225
|
}
|
|
225
226
|
None => Value::Null,
|
|
226
|
-
})
|
|
227
|
+
}),
|
|
227
228
|
);
|
|
228
229
|
let id_timeout = id;
|
|
229
230
|
obj.insert(
|
|
230
231
|
Arc::from("receiveTimeout"),
|
|
231
|
-
Value::
|
|
232
|
+
Value::native(move |args: &[Value]| {
|
|
232
233
|
let timeout_ms = args
|
|
233
234
|
.first()
|
|
234
235
|
.and_then(|v| match v {
|
|
@@ -242,13 +243,13 @@ fn conn_object(id: u32) -> Value {
|
|
|
242
243
|
Some(s) => {
|
|
243
244
|
let mut ev: ObjectMap = ObjectMap::default();
|
|
244
245
|
ev.insert(Arc::from("data"), Value::String(s.into()));
|
|
245
|
-
Value::Object(
|
|
246
|
+
Value::Object(VmRef::new(ev))
|
|
246
247
|
}
|
|
247
248
|
None => Value::Null,
|
|
248
249
|
}
|
|
249
|
-
})
|
|
250
|
+
}),
|
|
250
251
|
);
|
|
251
|
-
Value::Object(
|
|
252
|
+
Value::Object(VmRef::new(obj))
|
|
252
253
|
}
|
|
253
254
|
|
|
254
255
|
fn parse_port(args: &[Value]) -> Option<u16> {
|
|
@@ -472,9 +473,9 @@ pub fn web_socket_server_construct(args: &[Value]) -> Value {
|
|
|
472
473
|
}
|
|
473
474
|
|
|
474
475
|
// Node.js-compatible: server.clients is array of connected WebSocket instances
|
|
475
|
-
let clients:
|
|
476
|
+
let clients: VmRef<Vec<Value>> = VmRef::new(Vec::new());
|
|
476
477
|
|
|
477
|
-
let on_fn =
|
|
478
|
+
let on_fn = Value::native(|args: &[Value]| {
|
|
478
479
|
let Some(Value::Object(so)) = args.first() else {
|
|
479
480
|
return Value::Null;
|
|
480
481
|
};
|
|
@@ -489,8 +490,8 @@ pub fn web_socket_server_construct(args: &[Value]) -> Value {
|
|
|
489
490
|
Value::Null
|
|
490
491
|
});
|
|
491
492
|
|
|
492
|
-
let clients_listen =
|
|
493
|
-
let listen_fn =
|
|
493
|
+
let clients_listen = clients.clone();
|
|
494
|
+
let listen_fn = Value::native(move |args: &[Value]| {
|
|
494
495
|
let Some(Value::Object(so)) = args.first() else {
|
|
495
496
|
return Value::Null;
|
|
496
497
|
};
|
|
@@ -519,8 +520,8 @@ pub fn web_socket_server_construct(args: &[Value]) -> Value {
|
|
|
519
520
|
Value::Null
|
|
520
521
|
});
|
|
521
522
|
|
|
522
|
-
let clients_accept =
|
|
523
|
-
let accept_timeout_fn =
|
|
523
|
+
let clients_accept = clients.clone();
|
|
524
|
+
let accept_timeout_fn = Value::native(move |args: &[Value]| {
|
|
524
525
|
let Some(Value::Object(so)) = args.first() else {
|
|
525
526
|
return Value::Null;
|
|
526
527
|
};
|
|
@@ -541,13 +542,10 @@ pub fn web_socket_server_construct(args: &[Value]) -> Value {
|
|
|
541
542
|
m.insert(Arc::from("_handle"), handle_val);
|
|
542
543
|
m.insert(Arc::from("_onConnection"), Value::Null);
|
|
543
544
|
m.insert(Arc::from("clients"), Value::Array(clients));
|
|
544
|
-
m.insert(Arc::from("on"),
|
|
545
|
-
m.insert(Arc::from("listen"),
|
|
546
|
-
m.insert(
|
|
547
|
-
|
|
548
|
-
Value::Function(accept_timeout_fn),
|
|
549
|
-
);
|
|
550
|
-
Value::Object(Rc::new(RefCell::new(m)))
|
|
545
|
+
m.insert(Arc::from("on"), on_fn);
|
|
546
|
+
m.insert(Arc::from("listen"), listen_fn);
|
|
547
|
+
m.insert(Arc::from("acceptTimeout"), accept_timeout_fn);
|
|
548
|
+
Value::Object(VmRef::new(m))
|
|
551
549
|
}
|
|
552
550
|
|
|
553
551
|
#[cfg(test)]
|
|
@@ -562,7 +560,7 @@ mod tests {
|
|
|
562
560
|
let opts = {
|
|
563
561
|
let mut m: ObjectMap = ObjectMap::default();
|
|
564
562
|
m.insert(Arc::from("port"), Value::Number(port as f64));
|
|
565
|
-
Value::Object(
|
|
563
|
+
Value::Object(VmRef::new(m))
|
|
566
564
|
};
|
|
567
565
|
|
|
568
566
|
let handle = match web_socket_server_listen(std::slice::from_ref(&opts)) {
|
|
@@ -647,7 +645,7 @@ mod tests {
|
|
|
647
645
|
let opts = {
|
|
648
646
|
let mut m: ObjectMap = ObjectMap::default();
|
|
649
647
|
m.insert(Arc::from("port"), Value::Number(port as f64));
|
|
650
|
-
Value::Object(
|
|
648
|
+
Value::Object(VmRef::new(m))
|
|
651
649
|
};
|
|
652
650
|
|
|
653
651
|
let handle = match web_socket_server_listen(std::slice::from_ref(&opts)) {
|
|
@@ -679,8 +677,8 @@ mod tests {
|
|
|
679
677
|
if data.contains("\"type\":\"join\"") || data.contains("\"type\": \"join\"") {
|
|
680
678
|
let joined = r#"{"type":"joined","sessionId":"default"}"#;
|
|
681
679
|
let presence = r#"{"type":"presence","agentLanes":["ai-a"]}"#;
|
|
682
|
-
ws_send_native(&Value::Object(
|
|
683
|
-
ws_send_native(&Value::Object(
|
|
680
|
+
ws_send_native(&Value::Object(wso.clone()), joined);
|
|
681
|
+
ws_send_native(&Value::Object(wso.clone()), presence);
|
|
684
682
|
return;
|
|
685
683
|
}
|
|
686
684
|
}
|