@tishlang/tish 1.9.2 → 1.12.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/cli_help.rs +9 -1
- package/crates/tish/src/main.rs +66 -11
- 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 +74 -23
- 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/math.rs +7 -0
- package/crates/tish_builtins/src/object.rs +10 -10
- package/crates/tish_builtins/src/string.rs +27 -3
- package/crates/tish_builtins/src/symbol.rs +83 -0
- package/crates/tish_compile/src/codegen.rs +324 -158
- package/crates/tish_compile/src/lib.rs +39 -7
- package/crates/tish_compile/src/resolve.rs +191 -6
- 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 +213 -4
- package/crates/tish_cranelift/src/link.rs +1 -1
- 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 +270 -24
- package/crates/tish_native/src/config.rs +48 -0
- package/crates/tish_native/src/lib.rs +139 -12
- 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 +35 -44
- 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/timers.rs +12 -7
- 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 +5 -4
- package/crates/tish_ui/src/runtime/hooks.rs +123 -37
- package/crates/tish_ui/src/runtime/mod.rs +21 -41
- package/crates/tish_vm/Cargo.toml +2 -0
- package/crates/tish_vm/src/vm.rs +258 -153
- package/crates/tish_wasm/src/lib.rs +60 -7
- package/crates/tish_wasm_runtime/Cargo.toml +10 -1
- package/crates/tish_wasm_runtime/src/gpu.rs +413 -0
- package/crates/tish_wasm_runtime/src/lib.rs +7 -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.
|
|
@@ -48,7 +47,8 @@ pub use tishlang_builtins::string::{
|
|
|
48
47
|
pad_start as string_pad_start_impl, repeat as string_repeat_impl,
|
|
49
48
|
replace as string_replace_impl, replace_all as string_replace_all_impl,
|
|
50
49
|
slice as string_slice_impl, split as string_split_impl,
|
|
51
|
-
starts_with as string_starts_with_impl,
|
|
50
|
+
starts_with as string_starts_with_impl, substr as string_substr_impl,
|
|
51
|
+
substring as string_substring_impl,
|
|
52
52
|
to_lower_case as string_to_lower_case, to_upper_case as string_to_upper_case,
|
|
53
53
|
trim as string_trim,
|
|
54
54
|
};
|
|
@@ -106,6 +106,9 @@ pub fn string_slice(s: &Value, start: &Value, end: &Value) -> Value {
|
|
|
106
106
|
pub fn string_substring(s: &Value, start: &Value, end: &Value) -> Value {
|
|
107
107
|
string_substring_impl(s, start, end)
|
|
108
108
|
}
|
|
109
|
+
pub fn string_substr(s: &Value, start: &Value, length: &Value) -> Value {
|
|
110
|
+
string_substr_impl(s, start, length)
|
|
111
|
+
}
|
|
109
112
|
pub fn string_split(s: &Value, sep: &Value) -> Value {
|
|
110
113
|
string_split_impl(s, sep)
|
|
111
114
|
}
|
|
@@ -421,6 +424,7 @@ pub use tishlang_builtins::math::{
|
|
|
421
424
|
exp as tish_math_exp_impl, floor as tish_math_floor_impl, max as tish_math_max_impl,
|
|
422
425
|
min as tish_math_min_impl, pow as tish_math_pow_impl, random as tish_math_random_impl,
|
|
423
426
|
round as tish_math_round_impl, sign as tish_math_sign_impl, sin as tish_math_sin_impl,
|
|
427
|
+
imul as tish_math_imul_impl,
|
|
424
428
|
sqrt as tish_math_sqrt_impl, tan as tish_math_tan_impl, trunc as tish_math_trunc_impl,
|
|
425
429
|
};
|
|
426
430
|
|
|
@@ -461,6 +465,9 @@ pub fn math_exp(args: &[Value]) -> Value {
|
|
|
461
465
|
pub fn math_trunc(args: &[Value]) -> Value {
|
|
462
466
|
tish_math_trunc_impl(args)
|
|
463
467
|
}
|
|
468
|
+
pub fn math_imul(args: &[Value]) -> Value {
|
|
469
|
+
tish_math_imul_impl(args)
|
|
470
|
+
}
|
|
464
471
|
pub fn math_pow(args: &[Value]) -> Value {
|
|
465
472
|
tish_math_pow_impl(args)
|
|
466
473
|
}
|
|
@@ -590,8 +597,6 @@ pub fn is_dir(args: &[Value]) -> Value {
|
|
|
590
597
|
|
|
591
598
|
#[cfg(feature = "fs")]
|
|
592
599
|
pub fn read_dir(args: &[Value]) -> Value {
|
|
593
|
-
use std::cell::RefCell;
|
|
594
|
-
use std::rc::Rc;
|
|
595
600
|
let path = args
|
|
596
601
|
.first()
|
|
597
602
|
.map(|v| v.to_display_string())
|
|
@@ -632,7 +637,7 @@ pub fn get_prop(obj: &Value, key: impl AsRef<str>) -> Value {
|
|
|
632
637
|
// directly. Previously we allocated a fresh `Arc<str>` on
|
|
633
638
|
// every call (one heap alloc per `r.field` read in tight
|
|
634
639
|
// handler loops); this version is alloc-free on the hit path.
|
|
635
|
-
map.borrow().get(key).cloned().unwrap_or(Value::Null)
|
|
640
|
+
map.borrow().strings.get(key).cloned().unwrap_or(Value::Null)
|
|
636
641
|
}
|
|
637
642
|
Value::Array(arr) => {
|
|
638
643
|
if key == "length" {
|
|
@@ -685,14 +690,7 @@ pub fn get_index(obj: &Value, index: &Value) -> Value {
|
|
|
685
690
|
};
|
|
686
691
|
arr.borrow().get(idx).cloned().unwrap_or(Value::Null)
|
|
687
692
|
}
|
|
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
|
-
}
|
|
693
|
+
Value::Object(_) => tishlang_core::object_get(obj, index).unwrap_or(Value::Null),
|
|
696
694
|
_ => Value::Null,
|
|
697
695
|
}
|
|
698
696
|
}
|
|
@@ -705,10 +703,10 @@ pub fn set_prop(obj: &Value, key: &str, val: Value) -> Value {
|
|
|
705
703
|
// exists we re-use the existing `Arc<str>` and skip the
|
|
706
704
|
// alloc. Only newly-inserted keys pay for `Arc::from(key)`.
|
|
707
705
|
let mut m = map.borrow_mut();
|
|
708
|
-
if let Some(slot) = m.get_mut(key) {
|
|
706
|
+
if let Some(slot) = m.strings.get_mut(key) {
|
|
709
707
|
*slot = val.clone();
|
|
710
708
|
} else {
|
|
711
|
-
m.insert(Arc::from(key), val.clone());
|
|
709
|
+
m.strings.insert(Arc::from(key), val.clone());
|
|
712
710
|
}
|
|
713
711
|
val
|
|
714
712
|
}
|
|
@@ -731,13 +729,8 @@ pub fn set_index(obj: &Value, idx: &Value, val: Value) -> Value {
|
|
|
731
729
|
arr_mut[index] = val.clone();
|
|
732
730
|
val
|
|
733
731
|
}
|
|
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());
|
|
732
|
+
Value::Object(_) => {
|
|
733
|
+
tishlang_core::object_set(obj, idx, val.clone()).expect("object set");
|
|
741
734
|
val
|
|
742
735
|
}
|
|
743
736
|
_ => panic!("Cannot index assign on non-array/object"),
|
|
@@ -745,26 +738,24 @@ pub fn set_index(obj: &Value, idx: &Value, val: Value) -> Value {
|
|
|
745
738
|
}
|
|
746
739
|
|
|
747
740
|
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),
|
|
741
|
+
match obj {
|
|
742
|
+
Value::Object(_) => Value::Bool(tishlang_core::object_has(obj, key)),
|
|
756
743
|
Value::Array(arr) => {
|
|
757
|
-
key_str
|
|
744
|
+
let key_str: Arc<str> = match key {
|
|
745
|
+
Value::String(s) => Arc::clone(s),
|
|
746
|
+
Value::Number(n) => n.to_string().into(),
|
|
747
|
+
_ => return Value::Bool(false),
|
|
748
|
+
};
|
|
749
|
+
let result = key_str.as_ref() == "length"
|
|
758
750
|
|| key_str
|
|
759
751
|
.parse::<usize>()
|
|
760
752
|
.ok()
|
|
761
753
|
.map(|i| i < arr.borrow().len())
|
|
762
|
-
.unwrap_or(false)
|
|
754
|
+
.unwrap_or(false);
|
|
755
|
+
Value::Bool(result)
|
|
763
756
|
}
|
|
764
|
-
_ => false,
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
Value::Bool(result)
|
|
757
|
+
_ => Value::Bool(false),
|
|
758
|
+
}
|
|
768
759
|
}
|
|
769
760
|
|
|
770
761
|
// Object functions - delegate to tishlang_builtins::globals
|
|
@@ -809,7 +800,7 @@ mod http_fetch;
|
|
|
809
800
|
|
|
810
801
|
mod timers;
|
|
811
802
|
|
|
812
|
-
#[cfg(feature = "http")]
|
|
803
|
+
#[cfg(any(feature = "http", feature = "promise"))]
|
|
813
804
|
mod promise;
|
|
814
805
|
|
|
815
806
|
#[cfg(feature = "http")]
|
|
@@ -893,11 +884,11 @@ pub use timers::{
|
|
|
893
884
|
set_interval as timer_set_interval, set_timeout as timer_set_timeout,
|
|
894
885
|
};
|
|
895
886
|
|
|
896
|
-
#[cfg(feature = "http")]
|
|
897
|
-
pub use promise::{promise_instance_catch, promise_instance_then, promise_object};
|
|
887
|
+
#[cfg(any(feature = "http", feature = "promise"))]
|
|
888
|
+
pub use promise::{await_promise, promise_instance_catch, promise_instance_then, promise_object};
|
|
898
889
|
|
|
899
890
|
#[cfg(feature = "http")]
|
|
900
|
-
pub use native_promise::{
|
|
891
|
+
pub use native_promise::{fetch_all_promise, fetch_async_promise, fetch_promise};
|
|
901
892
|
|
|
902
893
|
// RegExp Support
|
|
903
894
|
#[cfg(feature = "regex")]
|
|
@@ -999,7 +990,7 @@ fn regexp_exec_impl(re: &mut tishlang_core::TishRegExp, input: &str) -> Value {
|
|
|
999
990
|
};
|
|
1000
991
|
}
|
|
1001
992
|
|
|
1002
|
-
Value::
|
|
993
|
+
Value::object(obj)
|
|
1003
994
|
}
|
|
1004
995
|
Ok(None) | Err(_) => {
|
|
1005
996
|
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 {
|
|
@@ -47,15 +47,20 @@ pub fn drain_timers() {
|
|
|
47
47
|
run_due_timers();
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
/// Run all due timer callbacks.
|
|
50
|
+
/// Run all due timer callbacks (including timers scheduled by other timers).
|
|
51
51
|
fn run_due_timers() {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if
|
|
55
|
-
|
|
52
|
+
for _ in 0..64 {
|
|
53
|
+
let due = take_due_timers();
|
|
54
|
+
if due.is_empty() {
|
|
55
|
+
break;
|
|
56
56
|
}
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
for (id, callback, args, interval_ms) in due {
|
|
58
|
+
if let Value::Function(f) = &callback {
|
|
59
|
+
let _ = f(&args);
|
|
60
|
+
}
|
|
61
|
+
if interval_ms > 0 {
|
|
62
|
+
re_register_interval(id, callback, args, interval_ms);
|
|
63
|
+
}
|
|
59
64
|
}
|
|
60
65
|
}
|
|
61
66
|
}
|