@tishlang/tish-lsp 2.38.0 → 2.43.8
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 +4 -1
- package/bin/tish-lsp +0 -0
- package/crates/tish/src/cli_help.rs +28 -0
- package/crates/tish/src/main.rs +41 -5
- package/crates/tish/tests/scope_merge.rs +79 -0
- package/crates/tish_build_utils/src/lib.rs +27 -1
- package/crates/tish_builtins/Cargo.toml +14 -6
- package/crates/tish_builtins/src/array.rs +130 -54
- package/crates/tish_builtins/src/collections.rs +9 -5
- package/crates/tish_builtins/src/construct.rs +7 -1
- package/crates/tish_builtins/src/date.rs +15 -1
- package/crates/tish_builtins/src/globals.rs +38 -3
- package/crates/tish_builtins/src/helpers.rs +7 -3
- package/crates/tish_builtins/src/iterator.rs +5 -1
- package/crates/tish_builtins/src/lib.rs +3 -0
- package/crates/tish_builtins/src/math.rs +14 -0
- package/crates/tish_builtins/src/number.rs +72 -6
- package/crates/tish_builtins/src/object.rs +6 -1
- package/crates/tish_builtins/src/string.rs +13 -2
- package/crates/tish_builtins/src/symbol.rs +17 -17
- package/crates/tish_builtins/src/typedarrays.rs +9 -2
- package/crates/tish_bytecode/src/compiler.rs +76 -2
- package/crates/tish_bytecode/src/lib.rs +1 -1
- package/crates/tish_bytecode/src/opcode.rs +77 -2
- package/crates/tish_compile/src/codegen.rs +812 -47
- package/crates/tish_compile/src/infer.rs +37 -41
- package/crates/tish_compile/src/lib.rs +13 -1
- package/crates/tish_compile/src/platform_resolve.rs +361 -0
- package/crates/tish_compile/src/resolve.rs +148 -10
- package/crates/tish_compile/src/schemes.rs +278 -0
- package/crates/tish_compile/src/types.rs +141 -2
- package/crates/tish_compile/tests/perf_codegen_320.rs +2 -2
- package/crates/tish_compile/tests/perf_codegen_module_const_forof.rs +3 -1
- package/crates/tish_compile/tests/platform_resolve_cli.rs +164 -0
- package/crates/tish_compile/tests/regr_gba_numerics_gated.rs +58 -0
- package/crates/tish_compiler_wasm/Cargo.toml +1 -0
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +56 -6
- package/crates/tish_core/Cargo.toml +39 -4
- package/crates/tish_core/src/compat.rs +506 -0
- package/crates/tish_core/src/console_style.rs +19 -1
- package/crates/tish_core/src/json.rs +34 -10
- package/crates/tish_core/src/lib.rs +121 -25
- package/crates/tish_core/src/macros.rs +1 -1
- package/crates/tish_core/src/shape.rs +4 -2
- package/crates/tish_core/src/uri.rs +8 -1
- package/crates/tish_core/src/value.rs +182 -54
- package/crates/tish_core/src/vmref.rs +17 -5
- package/crates/tish_eval/Cargo.toml +2 -0
- package/crates/tish_eval/src/eval.rs +90 -3
- package/crates/tish_eval/src/value.rs +8 -0
- package/crates/tish_eval/src/value_convert.rs +316 -21
- package/crates/tish_lsp/Cargo.toml +3 -1
- package/crates/tish_lsp/src/import_goto.rs +41 -14
- package/crates/tish_lsp/src/main.rs +18 -0
- package/crates/tish_native/src/build.rs +246 -0
- package/crates/tish_native/src/config.rs +20 -0
- package/crates/tish_native/src/lib.rs +16 -6
- package/crates/tish_parser/src/lib.rs +28 -0
- package/crates/tish_parser/src/parser.rs +25 -4
- package/crates/tish_runtime/src/http_hyper.rs +70 -0
- package/crates/tish_runtime/src/lib.rs +55 -10
- package/crates/tish_runtime/src/ws.rs +94 -21
- package/crates/tish_runtime_gba/Cargo.lock +837 -0
- package/crates/tish_runtime_gba/Cargo.toml +16 -0
- package/crates/tish_runtime_gba/src/gba.rs +211 -0
- package/crates/tish_runtime_gba/src/lib.rs +620 -0
- package/crates/tish_vm/src/jit.rs +1446 -164
- package/crates/tish_vm/src/vm.rs +535 -173
- package/justfile +9 -0
- package/package.json +1 -1
- package/platform/darwin-arm64/tish-lsp +0 -0
- package/platform/darwin-x64/tish-lsp +0 -0
- package/platform/linux-arm64/tish-lsp +0 -0
- package/platform/linux-x64/tish-lsp +0 -0
- package/platform/win32-x64/tish-lsp.exe +0 -0
|
@@ -16,6 +16,7 @@ pub use tishlang_core::ObjectMap;
|
|
|
16
16
|
// pass) instead of materializing an intermediate `AHashMap` and rebuilding from it.
|
|
17
17
|
pub use tishlang_core::{ObjectData, PropMap};
|
|
18
18
|
pub use tishlang_core::Value;
|
|
19
|
+
pub use tishlang_core::NumArrayBacking;
|
|
19
20
|
pub use tishlang_core::ArcStr;
|
|
20
21
|
/// Used by native codegen for `f()` / `obj()` dispatch (`Value::Function` or `__call` on objects).
|
|
21
22
|
/// Wraps `tishlang_core::value_call` with a pending-throw suppression check (#381): while a thrown
|
|
@@ -79,7 +80,7 @@ pub fn normalize_for_of(v: Value) -> Value {
|
|
|
79
80
|
// single-variant `if let Value::Array` in the spread / for-of codegen binds — otherwise it spreads
|
|
80
81
|
// to zero elements.
|
|
81
82
|
if let Value::NumberArray(arr) = &v {
|
|
82
|
-
let items: Vec<Value> = arr.borrow().
|
|
83
|
+
let items: Vec<Value> = arr.borrow().to_values();
|
|
83
84
|
return Value::Array(VmRef::new(items));
|
|
84
85
|
}
|
|
85
86
|
match tishlang_core::drain_iterator(&v) {
|
|
@@ -547,7 +548,8 @@ use tishlang_builtins::globals::{
|
|
|
547
548
|
decode_uri as builtins_decode_uri, encode_uri as builtins_encode_uri,
|
|
548
549
|
is_finite as builtins_is_finite, is_nan as builtins_is_nan,
|
|
549
550
|
object_assign as builtins_object_assign, object_entries as builtins_object_entries,
|
|
550
|
-
|
|
551
|
+
object_freeze as builtins_object_freeze, object_from_entries as builtins_object_from_entries,
|
|
552
|
+
object_is_frozen as builtins_object_is_frozen, object_keys as builtins_object_keys,
|
|
551
553
|
object_values as builtins_object_values,
|
|
552
554
|
number_convert as builtins_number_convert,
|
|
553
555
|
string_convert as builtins_string_convert,
|
|
@@ -1389,7 +1391,10 @@ pub fn get_prop(obj: &Value, key: impl AsRef<str>) -> Value {
|
|
|
1389
1391
|
if key == "length" {
|
|
1390
1392
|
Value::Number(arr.borrow().len() as f64)
|
|
1391
1393
|
} else if let Ok(idx) = key.parse::<usize>() {
|
|
1392
|
-
arr.borrow().get(idx).
|
|
1394
|
+
arr.borrow().get(idx).map(|v| match v {
|
|
1395
|
+
Value::Number(n) if n.is_nan() => Value::Null,
|
|
1396
|
+
other => other,
|
|
1397
|
+
}).unwrap_or(Value::Null)
|
|
1393
1398
|
} else {
|
|
1394
1399
|
Value::Null
|
|
1395
1400
|
}
|
|
@@ -1487,7 +1492,10 @@ pub fn get_index(obj: &Value, index: &Value) -> Value {
|
|
|
1487
1492
|
},
|
|
1488
1493
|
_ => return Value::Null,
|
|
1489
1494
|
};
|
|
1490
|
-
arr.borrow().get(idx).
|
|
1495
|
+
arr.borrow().get(idx).map(|v| match v {
|
|
1496
|
+
Value::Number(n) if n.is_nan() => Value::Null,
|
|
1497
|
+
other => other,
|
|
1498
|
+
}).unwrap_or(Value::Null)
|
|
1491
1499
|
}
|
|
1492
1500
|
// `str[i]` returns the character at index `i` (issue #17) — matches the VM /
|
|
1493
1501
|
// interpreter; out-of-bounds / negative / non-integer indices yield null.
|
|
@@ -1558,6 +1566,14 @@ pub fn set_prop(obj: &Value, key: &str, val: Value) -> Value {
|
|
|
1558
1566
|
// exists we re-use the existing `Arc<str>` and skip the
|
|
1559
1567
|
// alloc. Only newly-inserted keys pay for `Arc::from(key)`.
|
|
1560
1568
|
let mut m = map.borrow_mut();
|
|
1569
|
+
if m.frozen {
|
|
1570
|
+
// #437: a write to a frozen object throws a catchable TypeError (parked in the
|
|
1571
|
+
// pending-throw slot; the generated code surfaces it at the next checkpoint).
|
|
1572
|
+
tishlang_core::set_pending_throw(tishlang_core::type_error(format!(
|
|
1573
|
+
"Cannot assign to read only property '{key}' of a frozen object"
|
|
1574
|
+
)));
|
|
1575
|
+
return val;
|
|
1576
|
+
}
|
|
1561
1577
|
if let Some(slot) = m.strings.get_mut(key) {
|
|
1562
1578
|
*slot = val.clone();
|
|
1563
1579
|
} else {
|
|
@@ -1613,14 +1629,35 @@ pub fn set_index(obj: &Value, idx: &Value, val: Value) -> Value {
|
|
|
1613
1629
|
},
|
|
1614
1630
|
_ => panic!("Array index must be number"),
|
|
1615
1631
|
};
|
|
1616
|
-
let mut
|
|
1617
|
-
|
|
1618
|
-
|
|
1632
|
+
let mut b = arr.borrow_mut();
|
|
1633
|
+
match (b.as_packed_mut(), val.as_number()) {
|
|
1634
|
+
(Some(nums), Some(n)) => {
|
|
1635
|
+
while nums.len() <= index {
|
|
1636
|
+
nums.push(f64::NAN);
|
|
1637
|
+
}
|
|
1638
|
+
nums[index] = n;
|
|
1639
|
+
}
|
|
1640
|
+
// Non-numeric (or already deopted): upgrade in place, store the real Value.
|
|
1641
|
+
_ => {
|
|
1642
|
+
let boxed = b.deopt();
|
|
1643
|
+
while boxed.len() <= index {
|
|
1644
|
+
boxed.push(Value::Null);
|
|
1645
|
+
}
|
|
1646
|
+
boxed[index] = val.clone();
|
|
1647
|
+
}
|
|
1619
1648
|
}
|
|
1620
|
-
arr_mut[index] = val.as_number().unwrap_or(f64::NAN);
|
|
1621
1649
|
val
|
|
1622
1650
|
}
|
|
1623
|
-
Value::Object(
|
|
1651
|
+
Value::Object(map) => {
|
|
1652
|
+
if map.borrow().frozen {
|
|
1653
|
+
// #437: a write to a frozen object throws a catchable TypeError (parked; surfaced at
|
|
1654
|
+
// the next assignment checkpoint, same as `set_prop`).
|
|
1655
|
+
tishlang_core::set_pending_throw(tishlang_core::type_error(format!(
|
|
1656
|
+
"Cannot assign to read only property '{}' of a frozen object",
|
|
1657
|
+
idx.to_display_string()
|
|
1658
|
+
)));
|
|
1659
|
+
return val;
|
|
1660
|
+
}
|
|
1624
1661
|
tishlang_core::object_set(obj, idx, val.clone()).expect("object set");
|
|
1625
1662
|
val
|
|
1626
1663
|
}
|
|
@@ -1673,6 +1710,14 @@ pub fn object_keys(args: &[Value]) -> Value {
|
|
|
1673
1710
|
builtins_object_keys(args)
|
|
1674
1711
|
}
|
|
1675
1712
|
|
|
1713
|
+
pub fn object_freeze(args: &[Value]) -> Value {
|
|
1714
|
+
builtins_object_freeze(args)
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
pub fn object_is_frozen(args: &[Value]) -> Value {
|
|
1718
|
+
builtins_object_is_frozen(args)
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1676
1721
|
pub fn object_values(args: &[Value]) -> Value {
|
|
1677
1722
|
builtins_object_values(args)
|
|
1678
1723
|
}
|
|
@@ -1735,7 +1780,7 @@ pub use pty::{pty_kill, pty_pid, pty_read, pty_resize, pty_spawn, pty_write};
|
|
|
1735
1780
|
#[cfg(feature = "ws")]
|
|
1736
1781
|
pub use ws::{
|
|
1737
1782
|
web_socket_client, web_socket_server_accept, web_socket_server_construct,
|
|
1738
|
-
web_socket_server_listen, ws_broadcast_native, ws_send_native,
|
|
1783
|
+
web_socket_server_listen, ws_broadcast_native, ws_send_native, ws_serve_accept,
|
|
1739
1784
|
};
|
|
1740
1785
|
|
|
1741
1786
|
#[cfg(feature = "http")]
|
|
@@ -82,6 +82,83 @@ fn unregister(id: u32) {
|
|
|
82
82
|
CONNS.lock().unwrap().remove(&id);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/// Bridge an already-handshaked WebSocket stream into the `CONNS` registry — spawning the read and
|
|
86
|
+
/// write pump tasks — and return its connection id. Shared by the `tish:ws` server-accept loop and
|
|
87
|
+
/// the `serve()` HTTP→WS upgrade path (see `http_hyper.rs`). Must be called inside a tokio runtime.
|
|
88
|
+
pub(crate) fn register_ws_stream<S>(ws_stream: tokio_tungstenite::WebSocketStream<S>) -> u32
|
|
89
|
+
where
|
|
90
|
+
S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static,
|
|
91
|
+
{
|
|
92
|
+
let (send_tx, mut send_rx) = tokio_mpsc::unbounded_channel::<String>();
|
|
93
|
+
let (recv_tx, recv_rx) = mpsc::sync_channel::<String>(64);
|
|
94
|
+
let id = register(send_tx, recv_rx);
|
|
95
|
+
let recv_tx_task = Arc::new(recv_tx);
|
|
96
|
+
let (mut write, mut read) = ws_stream.split();
|
|
97
|
+
tokio::spawn(async move {
|
|
98
|
+
while let Some(Ok(msg)) = read.next().await {
|
|
99
|
+
if let tokio_tungstenite::tungstenite::Message::Text(t) = msg {
|
|
100
|
+
let _ = recv_tx_task.send(t.to_string());
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
unregister(id);
|
|
104
|
+
});
|
|
105
|
+
tokio::spawn(async move {
|
|
106
|
+
while let Some(text) = send_rx.recv().await {
|
|
107
|
+
let _ = write
|
|
108
|
+
.send(tokio_tungstenite::tungstenite::Message::Text(text.into()))
|
|
109
|
+
.await;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
id
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
lazy_static! {
|
|
116
|
+
/// Connections upgraded from HTTP by `serve()` (http_hyper), waiting for the program to pick them
|
|
117
|
+
/// up via `wsAccept`. Per-process: an upgraded socket stays in whatever SO_REUSEPORT worker
|
|
118
|
+
/// accepted it, so each worker drains its own queue and there's no cross-process routing problem.
|
|
119
|
+
static ref UPGRADE_QUEUE: (Mutex<std::collections::VecDeque<u32>>, std::sync::Condvar) =
|
|
120
|
+
(Mutex::new(std::collections::VecDeque::new()), std::sync::Condvar::new());
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/// Enqueue a `serve()`-upgraded connection id for `wsAccept` to hand to the program.
|
|
124
|
+
pub(crate) fn enqueue_upgraded_conn(id: u32) {
|
|
125
|
+
let (m, cv) = &*UPGRADE_QUEUE;
|
|
126
|
+
if let Ok(mut q) = m.lock() {
|
|
127
|
+
q.push_back(id);
|
|
128
|
+
}
|
|
129
|
+
cv.notify_all();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/// `wsAccept(timeoutMs?)` — return the next connection upgraded on the HTTP server's port (see the
|
|
133
|
+
/// `serve()` HTTP→WS upgrade path) as a normal `tish:ws` connection object, or `Null` if none arrives
|
|
134
|
+
/// within the timeout (default 0 = non-blocking).
|
|
135
|
+
pub fn ws_serve_accept(args: &[Value]) -> Value {
|
|
136
|
+
let timeout_ms = match args.first() {
|
|
137
|
+
Some(Value::Number(n)) if n.is_finite() && *n >= 0.0 => (*n as u64).min(3_600_000),
|
|
138
|
+
_ => 0,
|
|
139
|
+
};
|
|
140
|
+
let (m, cv) = &*UPGRADE_QUEUE;
|
|
141
|
+
let mut q = match m.lock() {
|
|
142
|
+
Ok(q) => q,
|
|
143
|
+
Err(_) => return Value::Null,
|
|
144
|
+
};
|
|
145
|
+
if q.is_empty() && timeout_ms > 0 {
|
|
146
|
+
let res = cv.wait_timeout_while(
|
|
147
|
+
q,
|
|
148
|
+
std::time::Duration::from_millis(timeout_ms),
|
|
149
|
+
|q| q.is_empty(),
|
|
150
|
+
);
|
|
151
|
+
q = match res {
|
|
152
|
+
Ok((g, _)) => g,
|
|
153
|
+
Err(e) => e.into_inner().0,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
match q.pop_front() {
|
|
157
|
+
Some(id) => conn_object(id),
|
|
158
|
+
None => Value::Null,
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
85
162
|
/// Max simultaneous WebSocket connections. Each accepted conn registers in `CONNS` and
|
|
86
163
|
/// spawns tasks + channels, freed only on close — unbounded accepts exhaust tasks/memory.
|
|
87
164
|
/// Override with `TISH_WS_MAX_CONNS`; default 10000.
|
|
@@ -399,27 +476,7 @@ pub fn web_socket_server_listen(args: &[Value]) -> Value {
|
|
|
399
476
|
continue;
|
|
400
477
|
}
|
|
401
478
|
};
|
|
402
|
-
let
|
|
403
|
-
let (recv_tx, recv_rx) = mpsc::sync_channel::<String>(64);
|
|
404
|
-
let id = register(send_tx, recv_rx);
|
|
405
|
-
let recv_tx = Arc::new(recv_tx);
|
|
406
|
-
let recv_tx_task = Arc::clone(&recv_tx);
|
|
407
|
-
let (mut write, mut read) = ws_stream.split();
|
|
408
|
-
tokio::spawn(async move {
|
|
409
|
-
while let Some(Ok(msg)) = read.next().await {
|
|
410
|
-
if let tokio_tungstenite::tungstenite::Message::Text(t) = msg {
|
|
411
|
-
let _ = recv_tx_task.send(t.to_string());
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
unregister(id);
|
|
415
|
-
});
|
|
416
|
-
tokio::spawn(async move {
|
|
417
|
-
while let Some(text) = send_rx.recv().await {
|
|
418
|
-
let _ = write
|
|
419
|
-
.send(tokio_tungstenite::tungstenite::Message::Text(text.into()))
|
|
420
|
-
.await;
|
|
421
|
-
}
|
|
422
|
-
});
|
|
479
|
+
let id = register_ws_stream(ws_stream);
|
|
423
480
|
if conn_tx.send(id).is_err() {
|
|
424
481
|
break;
|
|
425
482
|
}
|
|
@@ -578,6 +635,22 @@ mod tests {
|
|
|
578
635
|
use std::thread;
|
|
579
636
|
use std::time::Duration;
|
|
580
637
|
|
|
638
|
+
/// The serve() HTTP→WS upgrade queue: `wsAccept` hands out enqueued conns FIFO and returns Null
|
|
639
|
+
/// when empty. (End-to-end upgrade over a live hyper port is covered by the native smoke test.)
|
|
640
|
+
#[test]
|
|
641
|
+
fn upgrade_queue_drains_then_empty() {
|
|
642
|
+
assert!(matches!(ws_serve_accept(&[Value::Number(0.0)]), Value::Null));
|
|
643
|
+
enqueue_upgraded_conn(4242);
|
|
644
|
+
match ws_serve_accept(&[Value::Number(0.0)]) {
|
|
645
|
+
Value::Object(o) => {
|
|
646
|
+
let b = o.borrow();
|
|
647
|
+
assert!(matches!(b.strings.get("_id"), Some(Value::Number(n)) if *n == 4242.0));
|
|
648
|
+
}
|
|
649
|
+
other => panic!("expected a conn object, got {:?}", other),
|
|
650
|
+
}
|
|
651
|
+
assert!(matches!(ws_serve_accept(&[Value::Number(0.0)]), Value::Null));
|
|
652
|
+
}
|
|
653
|
+
|
|
581
654
|
#[test]
|
|
582
655
|
fn ws_echo_roundtrip() {
|
|
583
656
|
let port: u16 = 18_742;
|