@tishlang/tish 1.9.2 → 1.10.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/bin/tish +0 -0
- package/crates/js_to_tish/src/transform/expr.rs +8 -6
- package/crates/js_to_tish/src/transform/stmt.rs +12 -13
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/cargo_native_registry.rs +4 -1
- package/crates/tish/src/main.rs +11 -8
- package/crates/tish/tests/integration_test.rs +145 -7
- package/crates/tish_ast/src/ast.rs +3 -9
- package/crates/tish_build_utils/src/lib.rs +43 -15
- package/crates/tish_builtins/src/array.rs +2 -3
- package/crates/tish_builtins/src/construct.rs +15 -28
- package/crates/tish_builtins/src/globals.rs +18 -16
- package/crates/tish_builtins/src/helpers.rs +1 -4
- package/crates/tish_builtins/src/lib.rs +1 -0
- package/crates/tish_builtins/src/object.rs +10 -10
- package/crates/tish_builtins/src/string.rs +1 -3
- package/crates/tish_builtins/src/symbol.rs +83 -0
- package/crates/tish_compile/src/codegen.rs +123 -138
- package/crates/tish_compile/src/lib.rs +25 -3
- package/crates/tish_compile/src/resolve.rs +6 -3
- package/crates/tish_compile/src/types.rs +6 -6
- package/crates/tish_compile_js/src/codegen.rs +8 -5
- package/crates/tish_core/src/console_style.rs +9 -0
- package/crates/tish_core/src/json.rs +17 -7
- package/crates/tish_core/src/macros.rs +2 -2
- package/crates/tish_core/src/value.rs +192 -4
- package/crates/tish_cranelift_runtime/Cargo.toml +4 -0
- package/crates/tish_eval/src/eval.rs +135 -73
- package/crates/tish_eval/src/http.rs +18 -12
- package/crates/tish_eval/src/lib.rs +29 -0
- package/crates/tish_eval/src/regex.rs +1 -1
- package/crates/tish_eval/src/value.rs +89 -4
- package/crates/tish_eval/src/value_convert.rs +30 -8
- package/crates/tish_fmt/src/lib.rs +4 -1
- package/crates/tish_lexer/src/lib.rs +7 -2
- package/crates/tish_llvm/src/lib.rs +2 -2
- package/crates/tish_lsp/src/builtin_goto.rs +111 -10
- package/crates/tish_lsp/src/import_goto.rs +35 -22
- package/crates/tish_lsp/src/main.rs +118 -85
- package/crates/tish_native/src/build.rs +187 -10
- package/crates/tish_native/src/lib.rs +92 -8
- package/crates/tish_parser/src/lib.rs +5 -2
- package/crates/tish_parser/src/parser.rs +45 -75
- package/crates/tish_pg/src/error.rs +1 -1
- package/crates/tish_pg/src/lib.rs +61 -73
- package/crates/tish_resolve/src/lib.rs +283 -158
- package/crates/tish_resolve/src/pos.rs +10 -2
- package/crates/tish_runtime/Cargo.toml +3 -0
- package/crates/tish_runtime/src/http.rs +39 -39
- package/crates/tish_runtime/src/http_fetch.rs +12 -12
- package/crates/tish_runtime/src/lib.rs +26 -43
- package/crates/tish_runtime/src/native_promise.rs +0 -11
- package/crates/tish_runtime/src/promise.rs +14 -1
- package/crates/tish_runtime/src/promise_io.rs +1 -4
- package/crates/tish_runtime/src/ws.rs +40 -27
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +10 -8
- package/crates/tish_ui/src/jsx.rs +6 -4
- package/crates/tish_ui/src/lib.rs +2 -2
- package/crates/tish_ui/src/runtime/hooks.rs +5 -15
- package/crates/tish_ui/src/runtime/mod.rs +16 -17
- package/crates/tish_vm/Cargo.toml +2 -0
- package/crates/tish_vm/src/vm.rs +218 -153
- package/crates/tish_wasm/src/lib.rs +33 -7
- package/crates/tish_wasm_runtime/Cargo.toml +4 -1
- package/crates/tish_wasm_runtime/src/lib.rs +2 -1
- package/crates/tishlang_cargo_bindgen/src/classify.rs +1 -3
- package/crates/tishlang_cargo_bindgen/src/discover.rs +10 -5
- package/crates/tishlang_cargo_bindgen/src/infer.rs +18 -8
- package/crates/tishlang_cargo_bindgen/src/lib.rs +25 -26
- package/crates/tishlang_cargo_bindgen/src/main.rs +41 -38
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +4 -1
- package/justfile +3 -3
- 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
|
@@ -104,7 +104,12 @@ pub fn byte_offset_to_lsp(source: &str, byte: usize) -> Option<(u32, u32)> {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
/// True if LSP position lies inside `span` (half-open in byte space).
|
|
107
|
-
pub fn span_contains_lsp_position(
|
|
107
|
+
pub fn span_contains_lsp_position(
|
|
108
|
+
source: &str,
|
|
109
|
+
span: &Span,
|
|
110
|
+
lsp_line: u32,
|
|
111
|
+
lsp_character: u32,
|
|
112
|
+
) -> bool {
|
|
108
113
|
let Some(b) = lsp_position_to_byte_offset(source, lsp_line, lsp_character) else {
|
|
109
114
|
return false;
|
|
110
115
|
};
|
|
@@ -117,7 +122,10 @@ pub fn span_contains_lsp_position(source: &str, span: &Span, lsp_line: u32, lsp_
|
|
|
117
122
|
/// LSP start (inclusive) and end (exclusive) positions for a lexer span.
|
|
118
123
|
pub fn span_to_lsp_range_exclusive(source: &str, span: &Span) -> Option<((u32, u32), (u32, u32))> {
|
|
119
124
|
let (lo, hi) = lex_span_byte_range(source, span)?;
|
|
120
|
-
Some((
|
|
125
|
+
Some((
|
|
126
|
+
byte_offset_to_lsp(source, lo)?,
|
|
127
|
+
byte_offset_to_lsp(source, hi)?,
|
|
128
|
+
))
|
|
121
129
|
}
|
|
122
130
|
|
|
123
131
|
#[cfg(test)]
|
|
@@ -31,6 +31,9 @@ send-values = [
|
|
|
31
31
|
"tishlang_core/send-values",
|
|
32
32
|
"tishlang_builtins/send-values",
|
|
33
33
|
]
|
|
34
|
+
# Promise / `await` on settled values only (no `fetch` / HTTP stack). Used for WASI and other
|
|
35
|
+
# targets where the full `http` feature does not compile.
|
|
36
|
+
promise = ["send-values"]
|
|
34
37
|
# Phase-3 item 13: io_uring-backed accept + send loop (Linux only).
|
|
35
38
|
# Disabled by default; enable with `--feature http,http-io-uring` on Linux
|
|
36
39
|
# to pick up the experimental accept path.
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
//! gets its own tiny_http connection pool but the accept queue is shared at
|
|
11
11
|
//! the kernel level.
|
|
12
12
|
//!
|
|
13
|
-
//!
|
|
14
|
-
//! `!Send`, so handler execution stays on
|
|
13
|
+
//! Without `send-values`, the handler captures `Value::Function` backed by
|
|
14
|
+
//! `Rc` and is `!Send`, so handler execution stays on a single VM thread:
|
|
15
15
|
//!
|
|
16
16
|
//! ```text
|
|
17
17
|
//! worker 0 ──┐
|
|
@@ -24,14 +24,15 @@
|
|
|
24
24
|
//! threaded VM guarantee. Cached `Date:` header + shared `Arc<str>` response
|
|
25
25
|
//! bodies round out the hot-path optimisations.
|
|
26
26
|
|
|
27
|
-
use std::cell::RefCell;
|
|
28
|
-
use tishlang_core::VmRef;
|
|
29
|
-
use std::collections::VecDeque;
|
|
30
27
|
use std::fs::File;
|
|
31
28
|
use std::io::Write;
|
|
32
|
-
use std::rc::Rc;
|
|
33
29
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
|
34
|
-
use std::sync::{
|
|
30
|
+
use std::sync::{Arc, OnceLock};
|
|
31
|
+
|
|
32
|
+
#[cfg(not(feature = "send-values"))]
|
|
33
|
+
use std::collections::VecDeque;
|
|
34
|
+
#[cfg(not(feature = "send-values"))]
|
|
35
|
+
use std::sync::mpsc;
|
|
35
36
|
use std::thread;
|
|
36
37
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
|
37
38
|
|
|
@@ -64,17 +65,17 @@ where
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
pub fn await_fetch(args: Vec<Value>) -> Value {
|
|
67
|
-
crate::
|
|
68
|
+
crate::promise::await_promise(crate::native_promise::fetch_promise(args))
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
pub fn await_fetch_all(args: Vec<Value>) -> Value {
|
|
71
|
-
crate::
|
|
72
|
+
crate::promise::await_promise(crate::native_promise::fetch_all_promise(args))
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
pub(crate) fn extract_method(options: Option<&Value>) -> String {
|
|
75
76
|
options
|
|
76
77
|
.and_then(|v| match v {
|
|
77
|
-
Value::Object(obj) => obj.borrow().get(
|
|
78
|
+
Value::Object(obj) => obj.borrow().strings.get("method").cloned(),
|
|
78
79
|
_ => None,
|
|
79
80
|
})
|
|
80
81
|
.map(|v| v.to_display_string().to_uppercase())
|
|
@@ -84,12 +85,13 @@ pub(crate) fn extract_method(options: Option<&Value>) -> String {
|
|
|
84
85
|
pub(crate) fn extract_headers(options: Option<&Value>) -> Vec<(String, String)> {
|
|
85
86
|
options
|
|
86
87
|
.and_then(|v| match v {
|
|
87
|
-
Value::Object(obj) => obj.borrow().get(
|
|
88
|
+
Value::Object(obj) => obj.borrow().strings.get("headers").cloned(),
|
|
88
89
|
_ => None,
|
|
89
90
|
})
|
|
90
91
|
.map(|v| match v {
|
|
91
92
|
Value::Object(obj) => obj
|
|
92
93
|
.borrow()
|
|
94
|
+
.strings
|
|
93
95
|
.iter()
|
|
94
96
|
.map(|(k, v)| (k.to_string(), v.to_display_string()))
|
|
95
97
|
.collect(),
|
|
@@ -102,7 +104,8 @@ pub(crate) fn extract_body(options: Option<&Value>) -> Option<String> {
|
|
|
102
104
|
options.and_then(|v| match v {
|
|
103
105
|
Value::Object(obj) => obj
|
|
104
106
|
.borrow()
|
|
105
|
-
.
|
|
107
|
+
.strings
|
|
108
|
+
.get("body")
|
|
106
109
|
.map(|v| v.to_display_string()),
|
|
107
110
|
_ => None,
|
|
108
111
|
})
|
|
@@ -112,7 +115,7 @@ pub(crate) fn build_error_response(error: &str) -> Value {
|
|
|
112
115
|
let mut obj: ObjectMap = ObjectMap::with_capacity(2);
|
|
113
116
|
obj.insert(Arc::from("error"), Value::String(error.into()));
|
|
114
117
|
obj.insert(Arc::from("ok"), Value::Bool(false));
|
|
115
|
-
Value::
|
|
118
|
+
Value::object(obj)
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
// -------- cached Date header -----------------------------------------------
|
|
@@ -194,12 +197,6 @@ pub fn cached_date_header_arc() -> Arc<String> {
|
|
|
194
197
|
ensure_date_thread().load_full()
|
|
195
198
|
}
|
|
196
199
|
|
|
197
|
-
/// Back-compat String flavour (allocates). New hot-path code should use
|
|
198
|
-
/// `cached_date_header_arc`.
|
|
199
|
-
pub fn cached_date_header() -> String {
|
|
200
|
-
cached_date_header_arc().as_str().to_string()
|
|
201
|
-
}
|
|
202
|
-
|
|
203
200
|
// -------- Send-safe request/response primitives ----------------------------
|
|
204
201
|
|
|
205
202
|
#[derive(Debug, Clone)]
|
|
@@ -270,10 +267,10 @@ impl RequestPrimitive {
|
|
|
270
267
|
}
|
|
271
268
|
obj.insert(
|
|
272
269
|
Arc::clone(&keys.headers),
|
|
273
|
-
Value::
|
|
270
|
+
Value::object(h),
|
|
274
271
|
);
|
|
275
272
|
obj.insert(Arc::clone(&keys.body), Value::String(self.body.into()));
|
|
276
|
-
Value::
|
|
273
|
+
Value::object(obj)
|
|
277
274
|
})
|
|
278
275
|
}
|
|
279
276
|
}
|
|
@@ -331,16 +328,17 @@ impl ResponsePrimitive {
|
|
|
331
328
|
let obj_ref = obj.borrow();
|
|
332
329
|
|
|
333
330
|
let status = obj_ref
|
|
334
|
-
.
|
|
331
|
+
.strings
|
|
332
|
+
.get("status")
|
|
335
333
|
.and_then(|v| match v {
|
|
336
334
|
Value::Number(n) => Some(*n as u16),
|
|
337
335
|
_ => None,
|
|
338
336
|
})
|
|
339
337
|
.unwrap_or(default_status);
|
|
340
338
|
|
|
341
|
-
let has_error = obj_ref.contains_key(
|
|
339
|
+
let has_error = obj_ref.strings.contains_key("error");
|
|
342
340
|
|
|
343
|
-
let body: ResponseBody = if let Some(bb) = obj_ref.get(
|
|
341
|
+
let body: ResponseBody = if let Some(bb) = obj_ref.strings.get("bodyBytes") {
|
|
344
342
|
match bb {
|
|
345
343
|
Value::Array(a) => {
|
|
346
344
|
let v: Vec<u8> = a
|
|
@@ -355,9 +353,9 @@ impl ResponsePrimitive {
|
|
|
355
353
|
}
|
|
356
354
|
_ => ResponseBody::Text(Arc::from(bb.to_display_string())),
|
|
357
355
|
}
|
|
358
|
-
} else if let Some(b) = obj_ref.get(
|
|
356
|
+
} else if let Some(b) = obj_ref.strings.get("body") {
|
|
359
357
|
match b {
|
|
360
|
-
Value::String(s) => ResponseBody::Text(Arc::clone(s)),
|
|
358
|
+
Value::String(s) => ResponseBody::Text(Arc::clone(&s)),
|
|
361
359
|
Value::Array(a) => {
|
|
362
360
|
let borrow = a.borrow();
|
|
363
361
|
if !borrow.is_empty()
|
|
@@ -381,7 +379,8 @@ impl ResponsePrimitive {
|
|
|
381
379
|
} else if has_error {
|
|
382
380
|
ResponseBody::Text(Arc::from(
|
|
383
381
|
obj_ref
|
|
384
|
-
.
|
|
382
|
+
.strings
|
|
383
|
+
.get("error")
|
|
385
384
|
.map(|v| v.to_display_string())
|
|
386
385
|
.unwrap_or_default(),
|
|
387
386
|
))
|
|
@@ -396,10 +395,12 @@ impl ResponsePrimitive {
|
|
|
396
395
|
};
|
|
397
396
|
|
|
398
397
|
let headers = obj_ref
|
|
399
|
-
.
|
|
398
|
+
.strings
|
|
399
|
+
.get("headers")
|
|
400
400
|
.and_then(|v| match v {
|
|
401
401
|
Value::Object(h) => Some(
|
|
402
402
|
h.borrow()
|
|
403
|
+
.strings
|
|
403
404
|
.iter()
|
|
404
405
|
.map(|(k, v)| (k.to_string(), v.to_display_string()))
|
|
405
406
|
.collect(),
|
|
@@ -451,22 +452,25 @@ fn extract_file_from_response(value: &Value) -> Option<(u16, Vec<(String, String
|
|
|
451
452
|
return None;
|
|
452
453
|
};
|
|
453
454
|
let obj_ref = obj.borrow();
|
|
454
|
-
let Value::String(file_path) = obj_ref.get(
|
|
455
|
+
let Value::String(file_path) = obj_ref.strings.get("file")? else {
|
|
455
456
|
return None;
|
|
456
457
|
};
|
|
457
458
|
let file_path = file_path.to_string();
|
|
458
459
|
let status = obj_ref
|
|
459
|
-
.
|
|
460
|
+
.strings
|
|
461
|
+
.get("status")
|
|
460
462
|
.and_then(|v| match v {
|
|
461
463
|
Value::Number(n) => Some(*n as u16),
|
|
462
464
|
_ => None,
|
|
463
465
|
})
|
|
464
466
|
.unwrap_or(200);
|
|
465
467
|
let headers = obj_ref
|
|
466
|
-
.
|
|
468
|
+
.strings
|
|
469
|
+
.get("headers")
|
|
467
470
|
.and_then(|v| match v {
|
|
468
471
|
Value::Object(h) => Some(
|
|
469
472
|
h.borrow()
|
|
473
|
+
.strings
|
|
470
474
|
.iter()
|
|
471
475
|
.map(|(k, v)| (k.to_string(), v.to_display_string()))
|
|
472
476
|
.collect(),
|
|
@@ -770,8 +774,6 @@ pub(crate) fn num_prefork_workers() -> usize {
|
|
|
770
774
|
|
|
771
775
|
// -------- serve() ----------------------------------------------------------
|
|
772
776
|
|
|
773
|
-
type Job = (RequestPrimitive, mpsc::SyncSender<ResponsePrimitive>);
|
|
774
|
-
|
|
775
777
|
/// Start an HTTP server that handles requests using the provided handler function.
|
|
776
778
|
///
|
|
777
779
|
/// When `send-values` is enabled (required for the `http` feature) the
|
|
@@ -995,6 +997,9 @@ fn serve_impl_with_factory(
|
|
|
995
997
|
Value::Null
|
|
996
998
|
}
|
|
997
999
|
|
|
1000
|
+
#[cfg(not(feature = "send-values"))]
|
|
1001
|
+
type Job = (RequestPrimitive, mpsc::SyncSender<ResponsePrimitive>);
|
|
1002
|
+
|
|
998
1003
|
#[cfg(not(feature = "send-values"))]
|
|
999
1004
|
fn serve_impl<F>(args: &[Value], handler: F) -> Value
|
|
1000
1005
|
where
|
|
@@ -1130,6 +1135,7 @@ fn worker_loop_direct(
|
|
|
1130
1135
|
}
|
|
1131
1136
|
}
|
|
1132
1137
|
|
|
1138
|
+
#[cfg(not(feature = "send-values"))]
|
|
1133
1139
|
fn worker_loop(
|
|
1134
1140
|
listener: std::net::TcpListener,
|
|
1135
1141
|
dispatch: mpsc::SyncSender<Job>,
|
|
@@ -1202,12 +1208,6 @@ fn worker_loop(
|
|
|
1202
1208
|
}
|
|
1203
1209
|
}
|
|
1204
1210
|
|
|
1205
|
-
#[allow(dead_code)]
|
|
1206
|
-
pub fn create_server(port: u16) -> Result<tiny_http::Server, String> {
|
|
1207
|
-
let addr = format!("0.0.0.0:{}", port);
|
|
1208
|
-
tiny_http::Server::http(&addr).map_err(|e| format!("Failed to start server: {}", e))
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
1211
|
// -------- public shims for alternate HTTP backends ------------------------
|
|
1212
1212
|
//
|
|
1213
1213
|
// Exposed so `http_hyper.rs` (and any future backend) can reuse the same
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
//! Web Fetch–aligned Response, ReadableStream, reader.read(), text()/json().
|
|
2
2
|
|
|
3
|
-
use std::cell::RefCell;
|
|
4
|
-
use tishlang_core::VmRef;
|
|
5
3
|
use std::pin::Pin;
|
|
6
|
-
use std::rc::Rc;
|
|
7
4
|
use std::sync::{Arc, Mutex};
|
|
8
5
|
|
|
6
|
+
use tishlang_core::VmRef;
|
|
7
|
+
|
|
9
8
|
use bytes::Bytes;
|
|
10
9
|
use futures::Stream;
|
|
11
10
|
use futures::StreamExt;
|
|
@@ -87,19 +86,19 @@ impl TishPromise for ReadChunkPromise {
|
|
|
87
86
|
let mut o = ObjectMap::default();
|
|
88
87
|
o.insert(Arc::from("done"), Value::Bool(true));
|
|
89
88
|
o.insert(Arc::from("value"), Value::Null);
|
|
90
|
-
Ok(Value::
|
|
89
|
+
Ok(Value::object(o))
|
|
91
90
|
}
|
|
92
91
|
Ok(Ok(ReadChunk::Bytes(b))) => {
|
|
93
92
|
let arr: Vec<Value> = b.iter().map(|u| Value::Number(*u as f64)).collect();
|
|
94
93
|
let mut o = ObjectMap::default();
|
|
95
94
|
o.insert(Arc::from("done"), Value::Bool(false));
|
|
96
95
|
o.insert(Arc::from("value"), Value::Array(VmRef::new(arr)));
|
|
97
|
-
Ok(Value::
|
|
96
|
+
Ok(Value::object(o))
|
|
98
97
|
}
|
|
99
98
|
Ok(Err(e)) => Err({
|
|
100
99
|
let mut obj = ObjectMap::default();
|
|
101
100
|
obj.insert(Arc::from("error"), Value::String(e.into()));
|
|
102
|
-
Value::
|
|
101
|
+
Value::object(obj)
|
|
103
102
|
}),
|
|
104
103
|
Err(_) => Err(Value::String("Promise dropped".into())),
|
|
105
104
|
}
|
|
@@ -124,13 +123,13 @@ impl TishPromise for JsonTextPromise {
|
|
|
124
123
|
Err(e) => Err({
|
|
125
124
|
let mut obj = ObjectMap::default();
|
|
126
125
|
obj.insert(Arc::from("error"), Value::String(e.into()));
|
|
127
|
-
Value::
|
|
126
|
+
Value::object(obj)
|
|
128
127
|
}),
|
|
129
128
|
},
|
|
130
129
|
Ok(Err(e)) => Err({
|
|
131
130
|
let mut obj = ObjectMap::default();
|
|
132
131
|
obj.insert(Arc::from("error"), Value::String(e.into()));
|
|
133
|
-
Value::
|
|
132
|
+
Value::object(obj)
|
|
134
133
|
}),
|
|
135
134
|
Err(_) => Err(Value::String("Promise dropped".into())),
|
|
136
135
|
}
|
|
@@ -241,7 +240,7 @@ impl TishOpaque for HttpReadableStream {
|
|
|
241
240
|
Err(e) => {
|
|
242
241
|
let mut m = ObjectMap::default();
|
|
243
242
|
m.insert(Arc::from("error"), Value::String(e.into()));
|
|
244
|
-
Value::
|
|
243
|
+
Value::object(m)
|
|
245
244
|
}
|
|
246
245
|
}))
|
|
247
246
|
}
|
|
@@ -306,7 +305,7 @@ fn headers_to_value(headers: &reqwest::header::HeaderMap) -> Value {
|
|
|
306
305
|
headers_obj.insert(Arc::from(key.as_str()), Value::String(v.into()));
|
|
307
306
|
}
|
|
308
307
|
}
|
|
309
|
-
Value::
|
|
308
|
+
Value::object(headers_obj)
|
|
310
309
|
}
|
|
311
310
|
|
|
312
311
|
pub fn response_value_from_reqwest(response: reqwest::Response) -> Value {
|
|
@@ -351,7 +350,7 @@ pub fn response_value_from_reqwest(response: reqwest::Response) -> Value {
|
|
|
351
350
|
obj.insert(Arc::from("body"), body_stream_val);
|
|
352
351
|
obj.insert(Arc::from("text"), Value::Function(text_fn));
|
|
353
352
|
obj.insert(Arc::from("json"), Value::Function(json_fn));
|
|
354
|
-
Value::
|
|
353
|
+
Value::object(obj)
|
|
355
354
|
}
|
|
356
355
|
|
|
357
356
|
async fn send_request_parts(
|
|
@@ -424,7 +423,8 @@ pub fn fetch_all_promise_from_args(args: Vec<Value>) -> Value {
|
|
|
424
423
|
Value::Object(obj) => {
|
|
425
424
|
let obj_ref = obj.borrow();
|
|
426
425
|
match obj_ref
|
|
427
|
-
.
|
|
426
|
+
.strings
|
|
427
|
+
.get("url")
|
|
428
428
|
.map(|v| v.to_display_string())
|
|
429
429
|
{
|
|
430
430
|
Some(u) => (u, Some(req.clone())),
|
|
@@ -3,18 +3,17 @@
|
|
|
3
3
|
//! Re-exports core types from tishlang_core and provides console, Math,
|
|
4
4
|
//! and other builtin functions for compiled Tish programs.
|
|
5
5
|
|
|
6
|
-
#[cfg(feature = "regex")]
|
|
7
|
-
use std::cell::RefCell;
|
|
8
6
|
use std::fmt;
|
|
9
|
-
#[cfg(feature = "regex")]
|
|
10
|
-
use std::rc::Rc;
|
|
11
7
|
use std::sync::OnceLock;
|
|
12
8
|
use tishlang_builtins::helpers::extract_num;
|
|
13
9
|
#[cfg(feature = "fs")]
|
|
14
10
|
use tishlang_builtins::helpers::make_error_value;
|
|
15
11
|
|
|
12
|
+
pub use tishlang_builtins::symbol::symbol_object;
|
|
16
13
|
pub use tishlang_core::ObjectMap;
|
|
17
14
|
pub use tishlang_core::Value;
|
|
15
|
+
/// Used by native codegen for `f()` / `obj()` dispatch (`Value::Function` or `__call` on objects).
|
|
16
|
+
pub use tishlang_core::value_call;
|
|
18
17
|
// Re-export the shared-mutable wrapper so the Rust code emitted by
|
|
19
18
|
// `tishlang_compile::codegen` can write `VmRef::new(...)` without needing
|
|
20
19
|
// a direct dependency on `tishlang_core` from the generated crate.
|
|
@@ -590,8 +589,6 @@ pub fn is_dir(args: &[Value]) -> Value {
|
|
|
590
589
|
|
|
591
590
|
#[cfg(feature = "fs")]
|
|
592
591
|
pub fn read_dir(args: &[Value]) -> Value {
|
|
593
|
-
use std::cell::RefCell;
|
|
594
|
-
use std::rc::Rc;
|
|
595
592
|
let path = args
|
|
596
593
|
.first()
|
|
597
594
|
.map(|v| v.to_display_string())
|
|
@@ -632,7 +629,7 @@ pub fn get_prop(obj: &Value, key: impl AsRef<str>) -> Value {
|
|
|
632
629
|
// directly. Previously we allocated a fresh `Arc<str>` on
|
|
633
630
|
// every call (one heap alloc per `r.field` read in tight
|
|
634
631
|
// handler loops); this version is alloc-free on the hit path.
|
|
635
|
-
map.borrow().get(key).cloned().unwrap_or(Value::Null)
|
|
632
|
+
map.borrow().strings.get(key).cloned().unwrap_or(Value::Null)
|
|
636
633
|
}
|
|
637
634
|
Value::Array(arr) => {
|
|
638
635
|
if key == "length" {
|
|
@@ -685,14 +682,7 @@ pub fn get_index(obj: &Value, index: &Value) -> Value {
|
|
|
685
682
|
};
|
|
686
683
|
arr.borrow().get(idx).cloned().unwrap_or(Value::Null)
|
|
687
684
|
}
|
|
688
|
-
Value::Object(
|
|
689
|
-
let key: Arc<str> = match index {
|
|
690
|
-
Value::Number(n) => n.to_string().into(),
|
|
691
|
-
Value::String(s) => Arc::clone(s),
|
|
692
|
-
_ => return Value::Null,
|
|
693
|
-
};
|
|
694
|
-
map.borrow().get(&key).cloned().unwrap_or(Value::Null)
|
|
695
|
-
}
|
|
685
|
+
Value::Object(_) => tishlang_core::object_get(obj, index).unwrap_or(Value::Null),
|
|
696
686
|
_ => Value::Null,
|
|
697
687
|
}
|
|
698
688
|
}
|
|
@@ -705,10 +695,10 @@ pub fn set_prop(obj: &Value, key: &str, val: Value) -> Value {
|
|
|
705
695
|
// exists we re-use the existing `Arc<str>` and skip the
|
|
706
696
|
// alloc. Only newly-inserted keys pay for `Arc::from(key)`.
|
|
707
697
|
let mut m = map.borrow_mut();
|
|
708
|
-
if let Some(slot) = m.get_mut(key) {
|
|
698
|
+
if let Some(slot) = m.strings.get_mut(key) {
|
|
709
699
|
*slot = val.clone();
|
|
710
700
|
} else {
|
|
711
|
-
m.insert(Arc::from(key), val.clone());
|
|
701
|
+
m.strings.insert(Arc::from(key), val.clone());
|
|
712
702
|
}
|
|
713
703
|
val
|
|
714
704
|
}
|
|
@@ -731,13 +721,8 @@ pub fn set_index(obj: &Value, idx: &Value, val: Value) -> Value {
|
|
|
731
721
|
arr_mut[index] = val.clone();
|
|
732
722
|
val
|
|
733
723
|
}
|
|
734
|
-
Value::Object(
|
|
735
|
-
|
|
736
|
-
Value::Number(n) => n.to_string().into(),
|
|
737
|
-
Value::String(s) => Arc::clone(s),
|
|
738
|
-
_ => panic!("Object key must be string or number"),
|
|
739
|
-
};
|
|
740
|
-
map.borrow_mut().insert(key, val.clone());
|
|
724
|
+
Value::Object(_) => {
|
|
725
|
+
tishlang_core::object_set(obj, idx, val.clone()).expect("object set");
|
|
741
726
|
val
|
|
742
727
|
}
|
|
743
728
|
_ => panic!("Cannot index assign on non-array/object"),
|
|
@@ -745,26 +730,24 @@ pub fn set_index(obj: &Value, idx: &Value, val: Value) -> Value {
|
|
|
745
730
|
}
|
|
746
731
|
|
|
747
732
|
pub fn in_operator(key: &Value, obj: &Value) -> Value {
|
|
748
|
-
|
|
749
|
-
Value::
|
|
750
|
-
Value::Number(n) => n.to_string().into(),
|
|
751
|
-
_ => return Value::Bool(false),
|
|
752
|
-
};
|
|
753
|
-
|
|
754
|
-
let result = match obj {
|
|
755
|
-
Value::Object(map) => map.borrow().contains_key(&key_str),
|
|
733
|
+
match obj {
|
|
734
|
+
Value::Object(_) => Value::Bool(tishlang_core::object_has(obj, key)),
|
|
756
735
|
Value::Array(arr) => {
|
|
757
|
-
key_str
|
|
736
|
+
let key_str: Arc<str> = match key {
|
|
737
|
+
Value::String(s) => Arc::clone(s),
|
|
738
|
+
Value::Number(n) => n.to_string().into(),
|
|
739
|
+
_ => return Value::Bool(false),
|
|
740
|
+
};
|
|
741
|
+
let result = key_str.as_ref() == "length"
|
|
758
742
|
|| key_str
|
|
759
743
|
.parse::<usize>()
|
|
760
744
|
.ok()
|
|
761
745
|
.map(|i| i < arr.borrow().len())
|
|
762
|
-
.unwrap_or(false)
|
|
746
|
+
.unwrap_or(false);
|
|
747
|
+
Value::Bool(result)
|
|
763
748
|
}
|
|
764
|
-
_ => false,
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
Value::Bool(result)
|
|
749
|
+
_ => Value::Bool(false),
|
|
750
|
+
}
|
|
768
751
|
}
|
|
769
752
|
|
|
770
753
|
// Object functions - delegate to tishlang_builtins::globals
|
|
@@ -809,7 +792,7 @@ mod http_fetch;
|
|
|
809
792
|
|
|
810
793
|
mod timers;
|
|
811
794
|
|
|
812
|
-
#[cfg(feature = "http")]
|
|
795
|
+
#[cfg(any(feature = "http", feature = "promise"))]
|
|
813
796
|
mod promise;
|
|
814
797
|
|
|
815
798
|
#[cfg(feature = "http")]
|
|
@@ -893,11 +876,11 @@ pub use timers::{
|
|
|
893
876
|
set_interval as timer_set_interval, set_timeout as timer_set_timeout,
|
|
894
877
|
};
|
|
895
878
|
|
|
896
|
-
#[cfg(feature = "http")]
|
|
897
|
-
pub use promise::{promise_instance_catch, promise_instance_then, promise_object};
|
|
879
|
+
#[cfg(any(feature = "http", feature = "promise"))]
|
|
880
|
+
pub use promise::{await_promise, promise_instance_catch, promise_instance_then, promise_object};
|
|
898
881
|
|
|
899
882
|
#[cfg(feature = "http")]
|
|
900
|
-
pub use native_promise::{
|
|
883
|
+
pub use native_promise::{fetch_all_promise, fetch_async_promise, fetch_promise};
|
|
901
884
|
|
|
902
885
|
// RegExp Support
|
|
903
886
|
#[cfg(feature = "regex")]
|
|
@@ -999,7 +982,7 @@ fn regexp_exec_impl(re: &mut tishlang_core::TishRegExp, input: &str) -> Value {
|
|
|
999
982
|
};
|
|
1000
983
|
}
|
|
1001
984
|
|
|
1002
|
-
Value::
|
|
985
|
+
Value::object(obj)
|
|
1003
986
|
}
|
|
1004
987
|
Ok(None) | Err(_) => {
|
|
1005
988
|
if re.flags.global || re.flags.sticky {
|
|
@@ -13,14 +13,3 @@ pub fn fetch_all_promise(args: Vec<Value>) -> Value {
|
|
|
13
13
|
pub fn fetch_async_promise(args: Vec<Value>) -> Value {
|
|
14
14
|
fetch_promise(args)
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
pub fn await_promise(v: Value) -> Value {
|
|
18
|
-
if let Value::Promise(p) = v {
|
|
19
|
-
match p.block_until_settled() {
|
|
20
|
-
Ok(val) => val,
|
|
21
|
-
Err(rejection) => rejection,
|
|
22
|
-
}
|
|
23
|
-
} else {
|
|
24
|
-
v
|
|
25
|
-
}
|
|
26
|
-
}
|
|
@@ -213,7 +213,7 @@ pub fn promise_object() -> Value {
|
|
|
213
213
|
Arc::from("race"),
|
|
214
214
|
Value::native(|args: &[Value]| promise_race(args)),
|
|
215
215
|
);
|
|
216
|
-
Value::
|
|
216
|
+
Value::object(map)
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
/// `.then(onFulfilled, onRejected)` for a `Value::Promise` instance (VM `GetMember`).
|
|
@@ -233,3 +233,16 @@ pub fn promise_instance_catch(p: &Arc<dyn TishPromise>, args: &[Value]) -> Value
|
|
|
233
233
|
on_rejected: args.first().cloned(),
|
|
234
234
|
}))
|
|
235
235
|
}
|
|
236
|
+
|
|
237
|
+
/// Unwrap a settled [`Value::Promise`], or pass non-promise values through (VM `AwaitPromise` /
|
|
238
|
+
/// `tish:http.await`). Fetch promises still require the `http` feature.
|
|
239
|
+
pub fn await_promise(v: Value) -> Value {
|
|
240
|
+
if let Value::Promise(p) = v {
|
|
241
|
+
match p.block_until_settled() {
|
|
242
|
+
Ok(val) => val,
|
|
243
|
+
Err(rejection) => rejection,
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
v
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
//! Promises carrying only Send payloads (string results for text(), etc.).
|
|
2
2
|
|
|
3
|
-
use std::cell::RefCell;
|
|
4
|
-
use tishlang_core::VmRef;
|
|
5
|
-
use std::rc::Rc;
|
|
6
3
|
use std::sync::{Arc, Mutex};
|
|
7
4
|
use tishlang_core::{ObjectMap, TishPromise, Value};
|
|
8
5
|
use tokio::sync::oneshot;
|
|
@@ -11,7 +8,7 @@ fn error_value(msg: String) -> Value {
|
|
|
11
8
|
let mut obj: ObjectMap = ObjectMap::with_capacity(2);
|
|
12
9
|
obj.insert(Arc::from("error"), Value::String(msg.into()));
|
|
13
10
|
obj.insert(Arc::from("ok"), Value::Bool(false));
|
|
14
|
-
Value::
|
|
11
|
+
Value::object(obj)
|
|
15
12
|
}
|
|
16
13
|
|
|
17
14
|
pub struct StringResultPromise {
|