@tishlang/tish 1.6.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 +2 -0
- package/README.md +2 -0
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/error.rs +2 -8
- package/crates/js_to_tish/src/transform/expr.rs +128 -137
- package/crates/js_to_tish/src/transform/stmt.rs +62 -32
- package/crates/tish/Cargo.toml +15 -5
- package/crates/tish/src/cargo_native_registry.rs +29 -0
- package/crates/tish/src/cli_help.rs +92 -39
- package/crates/tish/src/main.rs +172 -86
- package/crates/tish/src/repl_completion.rs +3 -3
- package/crates/tish/tests/cargo_example_compile.rs +4 -2
- package/crates/tish/tests/integration_test.rs +216 -54
- package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
- package/crates/tish/tests/shortcircuit.rs +20 -5
- package/crates/tish_ast/src/ast.rs +92 -23
- package/crates/tish_build_utils/Cargo.toml +4 -0
- package/crates/tish_build_utils/src/lib.rs +136 -8
- package/crates/tish_builtins/Cargo.toml +5 -1
- package/crates/tish_builtins/src/array.rs +65 -33
- package/crates/tish_builtins/src/construct.rs +34 -39
- package/crates/tish_builtins/src/globals.rs +42 -26
- package/crates/tish_builtins/src/helpers.rs +2 -1
- package/crates/tish_builtins/src/lib.rs +5 -5
- package/crates/tish_builtins/src/math.rs +5 -3
- package/crates/tish_builtins/src/object.rs +3 -2
- package/crates/tish_builtins/src/string.rs +144 -22
- package/crates/tish_bytecode/src/chunk.rs +0 -1
- package/crates/tish_bytecode/src/compiler.rs +173 -71
- package/crates/tish_bytecode/src/opcode.rs +24 -6
- package/crates/tish_bytecode/src/peephole.rs +2 -2
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +1621 -453
- package/crates/tish_compile/src/infer.rs +75 -19
- package/crates/tish_compile/src/lib.rs +19 -8
- package/crates/tish_compile/src/resolve.rs +278 -137
- package/crates/tish_compile/src/types.rs +184 -24
- package/crates/tish_compile_js/Cargo.toml +1 -0
- package/crates/tish_compile_js/src/codegen.rs +181 -37
- package/crates/tish_compile_js/src/lib.rs +3 -1
- package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
- package/crates/tish_compiler_wasm/src/lib.rs +16 -13
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +69 -59
- package/crates/tish_core/Cargo.toml +8 -0
- package/crates/tish_core/src/json.rs +107 -56
- package/crates/tish_core/src/lib.rs +4 -2
- package/crates/tish_core/src/macros.rs +5 -5
- package/crates/tish_core/src/uri.rs +9 -6
- package/crates/tish_core/src/value.rs +145 -43
- package/crates/tish_core/src/vmref.rs +178 -0
- package/crates/tish_cranelift/src/link.rs +6 -9
- package/crates/tish_cranelift/src/lower.rs +14 -8
- package/crates/tish_eval/Cargo.toml +17 -2
- package/crates/tish_eval/src/eval.rs +474 -165
- package/crates/tish_eval/src/http.rs +61 -0
- package/crates/tish_eval/src/lib.rs +12 -8
- package/crates/tish_eval/src/natives.rs +136 -38
- package/crates/tish_eval/src/promise.rs +14 -8
- package/crates/tish_eval/src/timers.rs +28 -19
- package/crates/tish_eval/src/value.rs +17 -6
- package/crates/tish_eval/src/value_convert.rs +13 -5
- package/crates/tish_fmt/src/lib.rs +149 -43
- package/crates/tish_lexer/src/lib.rs +232 -63
- package/crates/tish_lexer/src/token.rs +10 -6
- package/crates/tish_llvm/src/lib.rs +17 -8
- 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 +504 -106
- package/crates/tish_native/src/build.rs +4 -8
- package/crates/tish_native/src/lib.rs +54 -21
- package/crates/tish_opt/src/lib.rs +84 -52
- package/crates/tish_parser/src/lib.rs +45 -13
- package/crates/tish_parser/src/parser.rs +505 -130
- 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 +1136 -145
- package/crates/tish_runtime/src/http_fetch.rs +38 -27
- 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 +375 -189
- package/crates/tish_runtime/src/promise.rs +199 -40
- 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 +65 -42
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
- package/crates/tish_ui/src/jsx.rs +317 -27
- 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 +725 -281
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +11 -4
- package/crates/tish_wasm/src/lib.rs +55 -42
- 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 +26 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +120 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +350 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
- 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
package/crates/tish_vm/src/vm.rs
CHANGED
|
@@ -1,24 +1,55 @@
|
|
|
1
1
|
//! Stack-based bytecode VM.
|
|
2
2
|
|
|
3
3
|
use std::cell::RefCell;
|
|
4
|
-
use
|
|
4
|
+
use tishlang_core::VmRef;
|
|
5
|
+
use std::collections::{HashMap, HashSet};
|
|
5
6
|
use std::rc::Rc;
|
|
6
7
|
use std::sync::Arc;
|
|
7
8
|
|
|
8
9
|
use tishlang_ast::{BinOp, UnaryOp};
|
|
9
10
|
use tishlang_builtins::array as arr_builtins;
|
|
10
11
|
use tishlang_builtins::construct as construct_builtin;
|
|
11
|
-
use tishlang_builtins::string as str_builtins;
|
|
12
12
|
use tishlang_builtins::globals as globals_builtins;
|
|
13
13
|
use tishlang_builtins::math as math_builtins;
|
|
14
|
+
use tishlang_builtins::string as str_builtins;
|
|
14
15
|
use tishlang_bytecode::{u8_to_binop, u8_to_unaryop, Chunk, Constant, Opcode, NO_REST_PARAM};
|
|
15
|
-
use tishlang_core::{ObjectMap, Value};
|
|
16
|
+
use tishlang_core::{NativeFn, ObjectMap, Value};
|
|
17
|
+
|
|
18
|
+
/// Wrap a closure in the right shared pointer for the current build.
|
|
19
|
+
/// Under `send-values` that's `Arc<dyn Fn + Send + Sync>`; otherwise it's
|
|
20
|
+
/// plain `Rc<dyn Fn>`. Call sites can stay ignorant of the distinction.
|
|
21
|
+
#[cfg(feature = "send-values")]
|
|
22
|
+
#[inline]
|
|
23
|
+
fn make_native_fn<F>(f: F) -> NativeFn
|
|
24
|
+
where
|
|
25
|
+
F: Fn(&[Value]) -> Value + Send + Sync + 'static,
|
|
26
|
+
{
|
|
27
|
+
Arc::new(f)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#[cfg(not(feature = "send-values"))]
|
|
31
|
+
#[inline]
|
|
32
|
+
fn make_native_fn<F>(f: F) -> NativeFn
|
|
33
|
+
where
|
|
34
|
+
F: Fn(&[Value]) -> Value + 'static,
|
|
35
|
+
{
|
|
36
|
+
Rc::new(f)
|
|
37
|
+
}
|
|
16
38
|
|
|
17
|
-
|
|
39
|
+
// Array / string / object methods have the same shape as `NativeFn`, which
|
|
40
|
+
// is already feature-gated (`Rc<dyn Fn>` vs `Arc<dyn Fn + Send + Sync>`).
|
|
41
|
+
// Alias to that so the VM picks the right pointer type automatically.
|
|
42
|
+
type ArrayMethodFn = NativeFn;
|
|
18
43
|
|
|
19
44
|
/// Feature names enabled for this VM run (`tish run --feature …`). `full` enables every optional capability.
|
|
20
45
|
#[cfg_attr(
|
|
21
|
-
not(any(
|
|
46
|
+
not(any(
|
|
47
|
+
feature = "fs",
|
|
48
|
+
feature = "http",
|
|
49
|
+
feature = "timers",
|
|
50
|
+
feature = "process",
|
|
51
|
+
feature = "ws"
|
|
52
|
+
)),
|
|
22
53
|
allow(dead_code)
|
|
23
54
|
)]
|
|
24
55
|
fn cap_allows(enabled: &HashSet<String>, name: &str) -> bool {
|
|
@@ -31,6 +62,8 @@ pub fn all_compiled_capabilities() -> HashSet<String> {
|
|
|
31
62
|
let mut s = HashSet::new();
|
|
32
63
|
#[cfg(feature = "http")]
|
|
33
64
|
s.insert("http".to_string());
|
|
65
|
+
#[cfg(feature = "timers")]
|
|
66
|
+
s.insert("timers".to_string());
|
|
34
67
|
#[cfg(feature = "fs")]
|
|
35
68
|
s.insert("fs".to_string());
|
|
36
69
|
#[cfg(feature = "process")]
|
|
@@ -44,19 +77,37 @@ pub fn all_compiled_capabilities() -> HashSet<String> {
|
|
|
44
77
|
|
|
45
78
|
/// Look up built-in module export for LoadNativeExport. Returns None if unknown or feature disabled.
|
|
46
79
|
#[cfg_attr(
|
|
47
|
-
not(any(
|
|
80
|
+
not(any(
|
|
81
|
+
feature = "fs",
|
|
82
|
+
feature = "http",
|
|
83
|
+
feature = "timers",
|
|
84
|
+
feature = "process",
|
|
85
|
+
feature = "ws"
|
|
86
|
+
)),
|
|
48
87
|
allow(unused_variables)
|
|
49
88
|
)]
|
|
50
89
|
fn get_builtin_export(enabled: &HashSet<String>, spec: &str, export_name: &str) -> Option<Value> {
|
|
51
90
|
#[cfg(feature = "fs")]
|
|
52
91
|
if spec == "tish:fs" && cap_allows(enabled, "fs") {
|
|
53
92
|
return match export_name {
|
|
54
|
-
"readFile" => Some(Value::
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
93
|
+
"readFile" => Some(Value::native(|args: &[Value]| {
|
|
94
|
+
tishlang_runtime::read_file(args)
|
|
95
|
+
})),
|
|
96
|
+
"writeFile" => Some(Value::native(|args: &[Value]| {
|
|
97
|
+
tishlang_runtime::write_file(args)
|
|
98
|
+
})),
|
|
99
|
+
"fileExists" => Some(Value::native(|args: &[Value]| {
|
|
100
|
+
tishlang_runtime::file_exists(args)
|
|
101
|
+
})),
|
|
102
|
+
"isDir" => Some(Value::native(|args: &[Value]| {
|
|
103
|
+
tishlang_runtime::is_dir(args)
|
|
104
|
+
})),
|
|
105
|
+
"readDir" => Some(Value::native(|args: &[Value]| {
|
|
106
|
+
tishlang_runtime::read_dir(args)
|
|
107
|
+
})),
|
|
108
|
+
"mkdir" => Some(Value::native(|args: &[Value]| {
|
|
109
|
+
tishlang_runtime::mkdir(args)
|
|
110
|
+
})),
|
|
60
111
|
_ => None,
|
|
61
112
|
};
|
|
62
113
|
}
|
|
@@ -64,60 +115,123 @@ fn get_builtin_export(enabled: &HashSet<String>, spec: &str, export_name: &str)
|
|
|
64
115
|
if spec == "tish:http" && cap_allows(enabled, "http") {
|
|
65
116
|
return match export_name {
|
|
66
117
|
// Bytecode compiler lowers `await expr` to `tish:http.await(promise)` (see tish_bytecode compiler).
|
|
67
|
-
"await" => Some(Value::
|
|
118
|
+
"await" => Some(Value::native(|args: &[Value]| {
|
|
68
119
|
tishlang_runtime::await_promise(args.first().cloned().unwrap_or(Value::Null))
|
|
69
|
-
}))
|
|
70
|
-
"fetch" => Some(Value::
|
|
120
|
+
})),
|
|
121
|
+
"fetch" => Some(Value::native(|args: &[Value]| {
|
|
71
122
|
tishlang_runtime::fetch_promise(args.to_vec())
|
|
72
|
-
}))
|
|
73
|
-
"fetchAll" => Some(Value::
|
|
123
|
+
})),
|
|
124
|
+
"fetchAll" => Some(Value::native(|args: &[Value]| {
|
|
74
125
|
tishlang_runtime::fetch_all_promise(args.to_vec())
|
|
75
|
-
}))
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
126
|
+
})),
|
|
127
|
+
"Promise" => Some(tishlang_runtime::promise_object()),
|
|
128
|
+
"serve" => Some(Value::native(|args: &[Value]| {
|
|
129
|
+
// Phase-1 item 2: support `serve(port, { handler, onWorker })`
|
|
130
|
+
// in addition to `serve(port, handler)`. When an options
|
|
131
|
+
// object is given and onWorker is a function, invoke it with
|
|
132
|
+
// worker id 0 and expect it to return the request handler.
|
|
133
|
+
let raw = args.get(1).cloned().unwrap_or(Value::Null);
|
|
134
|
+
let handler_value = match raw {
|
|
135
|
+
Value::Function(_) => raw,
|
|
136
|
+
Value::Object(ref obj) => {
|
|
137
|
+
let obj_ref = obj.borrow();
|
|
138
|
+
if let Some(Value::Function(on_worker)) =
|
|
139
|
+
obj_ref.get(&std::sync::Arc::from("onWorker")).cloned()
|
|
140
|
+
{
|
|
141
|
+
let args_for_init = [Value::Number(0.0)];
|
|
142
|
+
on_worker(&args_for_init)
|
|
143
|
+
} else if let Some(h) =
|
|
144
|
+
obj_ref.get(&std::sync::Arc::from("handler")).cloned()
|
|
145
|
+
{
|
|
146
|
+
h
|
|
147
|
+
} else {
|
|
148
|
+
Value::Null
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
_ => Value::Null,
|
|
152
|
+
};
|
|
153
|
+
if let Value::Function(f) = handler_value {
|
|
79
154
|
tishlang_runtime::http_serve(args, move |req_args| f(req_args))
|
|
80
155
|
} else {
|
|
81
156
|
Value::Null
|
|
82
157
|
}
|
|
83
|
-
}))
|
|
158
|
+
})),
|
|
159
|
+
_ => None,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
#[cfg(feature = "timers")]
|
|
163
|
+
if spec == "tish:timers" && cap_allows(enabled, "timers") {
|
|
164
|
+
return match export_name {
|
|
165
|
+
"setTimeout" => Some(Value::native(|args: &[Value]| {
|
|
166
|
+
tishlang_runtime::timer_set_timeout(args)
|
|
167
|
+
})),
|
|
168
|
+
"setInterval" => Some(Value::native(|args: &[Value]| {
|
|
169
|
+
tishlang_runtime::timer_set_interval(args)
|
|
170
|
+
})),
|
|
171
|
+
"clearTimeout" => Some(Value::native(|args: &[Value]| {
|
|
172
|
+
tishlang_runtime::timer_clear_timeout(args)
|
|
173
|
+
})),
|
|
174
|
+
"clearInterval" => Some(Value::native(|args: &[Value]| {
|
|
175
|
+
tishlang_runtime::timer_clear_interval(args)
|
|
176
|
+
})),
|
|
84
177
|
_ => None,
|
|
85
178
|
};
|
|
86
179
|
}
|
|
87
180
|
#[cfg(feature = "process")]
|
|
88
181
|
if spec == "tish:process" && cap_allows(enabled, "process") {
|
|
89
182
|
return match export_name {
|
|
90
|
-
"exit" => Some(Value::
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
"
|
|
183
|
+
"exit" => Some(Value::native(|args: &[Value]| {
|
|
184
|
+
tishlang_runtime::process_exit(args)
|
|
185
|
+
})),
|
|
186
|
+
"cwd" => Some(Value::native(|args: &[Value]| {
|
|
187
|
+
tishlang_runtime::process_cwd(args)
|
|
188
|
+
})),
|
|
189
|
+
"exec" => Some(Value::native(|args: &[Value]| {
|
|
190
|
+
tishlang_runtime::process_exec(args)
|
|
191
|
+
})),
|
|
192
|
+
"argv" => Some(Value::Array(VmRef::new(
|
|
94
193
|
std::env::args().map(|s| Value::String(s.into())).collect(),
|
|
95
|
-
)))
|
|
96
|
-
"env" => Some(Value::Object(
|
|
194
|
+
))),
|
|
195
|
+
"env" => Some(Value::Object(VmRef::new(
|
|
97
196
|
std::env::vars()
|
|
98
197
|
.map(|(k, v)| (Arc::from(k.as_str()), Value::String(v.into())))
|
|
99
198
|
.collect(),
|
|
100
|
-
)))
|
|
199
|
+
))),
|
|
101
200
|
"process" => {
|
|
102
201
|
let mut m = ObjectMap::default();
|
|
103
|
-
m.insert(
|
|
104
|
-
|
|
105
|
-
|
|
202
|
+
m.insert(
|
|
203
|
+
"exit".into(),
|
|
204
|
+
Value::native(|args: &[Value]| {
|
|
205
|
+
tishlang_runtime::process_exit(args)
|
|
206
|
+
}),
|
|
207
|
+
);
|
|
208
|
+
m.insert(
|
|
209
|
+
"cwd".into(),
|
|
210
|
+
Value::native(|args: &[Value]| {
|
|
211
|
+
tishlang_runtime::process_cwd(args)
|
|
212
|
+
}),
|
|
213
|
+
);
|
|
214
|
+
m.insert(
|
|
215
|
+
"exec".into(),
|
|
216
|
+
Value::native(|args: &[Value]| {
|
|
217
|
+
tishlang_runtime::process_exec(args)
|
|
218
|
+
}),
|
|
219
|
+
);
|
|
106
220
|
m.insert(
|
|
107
221
|
"argv".into(),
|
|
108
|
-
Value::Array(
|
|
222
|
+
Value::Array(VmRef::new(
|
|
109
223
|
std::env::args().map(|s| Value::String(s.into())).collect(),
|
|
110
|
-
))
|
|
224
|
+
)),
|
|
111
225
|
);
|
|
112
226
|
m.insert(
|
|
113
227
|
"env".into(),
|
|
114
|
-
Value::Object(
|
|
228
|
+
Value::Object(VmRef::new(
|
|
115
229
|
std::env::vars()
|
|
116
230
|
.map(|(k, v)| (Arc::from(k.as_str()), Value::String(v.into())))
|
|
117
231
|
.collect(),
|
|
118
|
-
))
|
|
232
|
+
)),
|
|
119
233
|
);
|
|
120
|
-
Some(Value::Object(
|
|
234
|
+
Some(Value::Object(VmRef::new(m)))
|
|
121
235
|
}
|
|
122
236
|
_ => None,
|
|
123
237
|
};
|
|
@@ -125,21 +239,24 @@ fn get_builtin_export(enabled: &HashSet<String>, spec: &str, export_name: &str)
|
|
|
125
239
|
#[cfg(feature = "ws")]
|
|
126
240
|
if spec == "tish:ws" && cap_allows(enabled, "ws") {
|
|
127
241
|
return match export_name {
|
|
128
|
-
"WebSocket" => Some(Value::
|
|
242
|
+
"WebSocket" => Some(Value::native(|args: &[Value]| {
|
|
129
243
|
tishlang_runtime::web_socket_client(args)
|
|
130
|
-
}))
|
|
131
|
-
"Server" => Some(Value::
|
|
244
|
+
})),
|
|
245
|
+
"Server" => Some(Value::native(|args: &[Value]| {
|
|
132
246
|
tishlang_runtime::web_socket_server_construct(args)
|
|
133
|
-
}))
|
|
134
|
-
"wsSend" => Some(Value::
|
|
247
|
+
})),
|
|
248
|
+
"wsSend" => Some(Value::native(|args: &[Value]| {
|
|
135
249
|
Value::Bool(tishlang_runtime::ws_send_native(
|
|
136
250
|
args.first().unwrap_or(&Value::Null),
|
|
137
|
-
&args
|
|
251
|
+
&args
|
|
252
|
+
.get(1)
|
|
253
|
+
.map(|v| v.to_display_string())
|
|
254
|
+
.unwrap_or_default(),
|
|
138
255
|
))
|
|
139
|
-
}))
|
|
140
|
-
"wsBroadcast" => Some(Value::
|
|
256
|
+
})),
|
|
257
|
+
"wsBroadcast" => Some(Value::native(|args: &[Value]| {
|
|
141
258
|
tishlang_runtime::ws_broadcast_native(args)
|
|
142
|
-
}))
|
|
259
|
+
})),
|
|
143
260
|
_ => None,
|
|
144
261
|
};
|
|
145
262
|
}
|
|
@@ -182,159 +299,223 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
|
|
|
182
299
|
let mut console = ObjectMap::default();
|
|
183
300
|
console.insert(
|
|
184
301
|
"debug".into(),
|
|
185
|
-
Value::
|
|
186
|
-
let s =
|
|
302
|
+
Value::native(|args: &[Value]| {
|
|
303
|
+
let s =
|
|
304
|
+
tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
|
|
187
305
|
vm_log(&s);
|
|
188
306
|
Value::Null
|
|
189
|
-
})
|
|
307
|
+
}),
|
|
190
308
|
);
|
|
191
309
|
console.insert(
|
|
192
310
|
"log".into(),
|
|
193
|
-
Value::
|
|
194
|
-
let s =
|
|
311
|
+
Value::native(|args: &[Value]| {
|
|
312
|
+
let s =
|
|
313
|
+
tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
|
|
195
314
|
vm_log(&s);
|
|
196
315
|
Value::Null
|
|
197
|
-
})
|
|
316
|
+
}),
|
|
198
317
|
);
|
|
199
318
|
console.insert(
|
|
200
319
|
"info".into(),
|
|
201
|
-
Value::
|
|
202
|
-
let s =
|
|
320
|
+
Value::native(|args: &[Value]| {
|
|
321
|
+
let s =
|
|
322
|
+
tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
|
|
203
323
|
vm_log(&s);
|
|
204
324
|
Value::Null
|
|
205
|
-
})
|
|
325
|
+
}),
|
|
206
326
|
);
|
|
207
327
|
console.insert(
|
|
208
328
|
"warn".into(),
|
|
209
|
-
Value::
|
|
210
|
-
let s =
|
|
329
|
+
Value::native(|args: &[Value]| {
|
|
330
|
+
let s =
|
|
331
|
+
tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
|
|
211
332
|
vm_log_err(&s);
|
|
212
333
|
Value::Null
|
|
213
|
-
})
|
|
334
|
+
}),
|
|
214
335
|
);
|
|
215
336
|
console.insert(
|
|
216
337
|
"error".into(),
|
|
217
|
-
Value::
|
|
218
|
-
let s =
|
|
338
|
+
Value::native(|args: &[Value]| {
|
|
339
|
+
let s =
|
|
340
|
+
tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
|
|
219
341
|
vm_log_err(&s);
|
|
220
342
|
Value::Null
|
|
221
|
-
})
|
|
343
|
+
}),
|
|
344
|
+
);
|
|
345
|
+
g.insert(
|
|
346
|
+
"console".into(),
|
|
347
|
+
Value::Object(VmRef::new(console)),
|
|
222
348
|
);
|
|
223
|
-
g.insert("console".into(), Value::Object(Rc::new(RefCell::new(console))));
|
|
224
349
|
|
|
225
350
|
let mut math = ObjectMap::default();
|
|
226
351
|
math.insert(
|
|
227
352
|
"abs".into(),
|
|
228
|
-
Value::
|
|
353
|
+
Value::native(|args: &[Value]| {
|
|
229
354
|
let n = args.first().and_then(|v| v.as_number()).unwrap_or(f64::NAN);
|
|
230
355
|
Value::Number(n.abs())
|
|
231
|
-
})
|
|
356
|
+
}),
|
|
232
357
|
);
|
|
233
358
|
math.insert(
|
|
234
359
|
"sqrt".into(),
|
|
235
|
-
Value::
|
|
360
|
+
Value::native(|args: &[Value]| {
|
|
236
361
|
let n = args.first().and_then(|v| v.as_number()).unwrap_or(f64::NAN);
|
|
237
362
|
Value::Number(n.sqrt())
|
|
238
|
-
})
|
|
363
|
+
}),
|
|
239
364
|
);
|
|
240
365
|
math.insert(
|
|
241
366
|
"floor".into(),
|
|
242
|
-
Value::
|
|
367
|
+
Value::native(|args: &[Value]| {
|
|
243
368
|
let n = args.first().and_then(|v| v.as_number()).unwrap_or(f64::NAN);
|
|
244
369
|
Value::Number(n.floor())
|
|
245
|
-
})
|
|
370
|
+
}),
|
|
246
371
|
);
|
|
247
372
|
math.insert(
|
|
248
373
|
"ceil".into(),
|
|
249
|
-
Value::
|
|
374
|
+
Value::native(|args: &[Value]| {
|
|
250
375
|
let n = args.first().and_then(|v| v.as_number()).unwrap_or(f64::NAN);
|
|
251
376
|
Value::Number(n.ceil())
|
|
252
|
-
})
|
|
377
|
+
}),
|
|
253
378
|
);
|
|
254
379
|
math.insert(
|
|
255
380
|
"round".into(),
|
|
256
|
-
Value::
|
|
381
|
+
Value::native(|args: &[Value]| {
|
|
257
382
|
let n = args.first().and_then(|v| v.as_number()).unwrap_or(f64::NAN);
|
|
258
383
|
Value::Number(n.round())
|
|
259
|
-
})
|
|
384
|
+
}),
|
|
260
385
|
);
|
|
261
386
|
math.insert(
|
|
262
387
|
"random".into(),
|
|
263
|
-
Value::
|
|
388
|
+
Value::native(|_| Value::Number(rand::random::<f64>())),
|
|
264
389
|
);
|
|
265
390
|
math.insert(
|
|
266
391
|
"min".into(),
|
|
267
|
-
Value::
|
|
392
|
+
Value::native(|args: &[Value]| {
|
|
268
393
|
let nums: Vec<f64> = args.iter().filter_map(|v| v.as_number()).collect();
|
|
269
394
|
Value::Number(nums.into_iter().fold(f64::NAN, |a, b| a.min(b)))
|
|
270
|
-
})
|
|
395
|
+
}),
|
|
271
396
|
);
|
|
272
397
|
math.insert(
|
|
273
398
|
"max".into(),
|
|
274
|
-
Value::
|
|
399
|
+
Value::native(|args: &[Value]| {
|
|
275
400
|
let nums: Vec<f64> = args.iter().filter_map(|v| v.as_number()).collect();
|
|
276
401
|
Value::Number(nums.into_iter().fold(f64::NAN, |a, b| a.max(b)))
|
|
277
|
-
})
|
|
402
|
+
}),
|
|
403
|
+
);
|
|
404
|
+
math.insert(
|
|
405
|
+
"pow".into(),
|
|
406
|
+
Value::native(|args: &[Value]| math_builtins::pow(args)),
|
|
407
|
+
);
|
|
408
|
+
math.insert(
|
|
409
|
+
"sin".into(),
|
|
410
|
+
Value::native(|args: &[Value]| math_builtins::sin(args)),
|
|
411
|
+
);
|
|
412
|
+
math.insert(
|
|
413
|
+
"cos".into(),
|
|
414
|
+
Value::native(|args: &[Value]| math_builtins::cos(args)),
|
|
415
|
+
);
|
|
416
|
+
math.insert(
|
|
417
|
+
"tan".into(),
|
|
418
|
+
Value::native(|args: &[Value]| math_builtins::tan(args)),
|
|
419
|
+
);
|
|
420
|
+
math.insert(
|
|
421
|
+
"log".into(),
|
|
422
|
+
Value::native(|args: &[Value]| math_builtins::log(args)),
|
|
423
|
+
);
|
|
424
|
+
math.insert(
|
|
425
|
+
"exp".into(),
|
|
426
|
+
Value::native(|args: &[Value]| math_builtins::exp(args)),
|
|
427
|
+
);
|
|
428
|
+
math.insert(
|
|
429
|
+
"sign".into(),
|
|
430
|
+
Value::native(|args: &[Value]| math_builtins::sign(args)),
|
|
431
|
+
);
|
|
432
|
+
math.insert(
|
|
433
|
+
"trunc".into(),
|
|
434
|
+
Value::native(|args: &[Value]| math_builtins::trunc(args)),
|
|
278
435
|
);
|
|
279
|
-
math.insert("pow".into(), Value::Function(Rc::new(|args: &[Value]| math_builtins::pow(args))));
|
|
280
|
-
math.insert("sin".into(), Value::Function(Rc::new(|args: &[Value]| math_builtins::sin(args))));
|
|
281
|
-
math.insert("cos".into(), Value::Function(Rc::new(|args: &[Value]| math_builtins::cos(args))));
|
|
282
|
-
math.insert("tan".into(), Value::Function(Rc::new(|args: &[Value]| math_builtins::tan(args))));
|
|
283
|
-
math.insert("log".into(), Value::Function(Rc::new(|args: &[Value]| math_builtins::log(args))));
|
|
284
|
-
math.insert("exp".into(), Value::Function(Rc::new(|args: &[Value]| math_builtins::exp(args))));
|
|
285
|
-
math.insert("sign".into(), Value::Function(Rc::new(|args: &[Value]| math_builtins::sign(args))));
|
|
286
|
-
math.insert("trunc".into(), Value::Function(Rc::new(|args: &[Value]| math_builtins::trunc(args))));
|
|
287
436
|
math.insert("PI".into(), Value::Number(std::f64::consts::PI));
|
|
288
437
|
math.insert("E".into(), Value::Number(std::f64::consts::E));
|
|
289
|
-
g.insert("Math".into(), Value::Object(
|
|
438
|
+
g.insert("Math".into(), Value::Object(VmRef::new(math)));
|
|
290
439
|
|
|
291
440
|
let mut json = ObjectMap::default();
|
|
292
441
|
json.insert(
|
|
293
442
|
"parse".into(),
|
|
294
|
-
Value::
|
|
295
|
-
let s = args
|
|
443
|
+
Value::native(|args: &[Value]| {
|
|
444
|
+
let s = args
|
|
445
|
+
.first()
|
|
446
|
+
.map(|v| v.to_display_string())
|
|
447
|
+
.unwrap_or_default();
|
|
296
448
|
tishlang_core::json_parse(&s).unwrap_or(Value::Null)
|
|
297
|
-
})
|
|
449
|
+
}),
|
|
298
450
|
);
|
|
299
451
|
json.insert(
|
|
300
452
|
"stringify".into(),
|
|
301
|
-
Value::
|
|
453
|
+
Value::native(|args: &[Value]| {
|
|
302
454
|
let v = args.first().unwrap_or(&Value::Null);
|
|
303
455
|
Value::String(tishlang_core::json_stringify(v).into())
|
|
304
|
-
})
|
|
456
|
+
}),
|
|
305
457
|
);
|
|
306
|
-
g.insert("JSON".into(), Value::Object(
|
|
458
|
+
g.insert("JSON".into(), Value::Object(VmRef::new(json)));
|
|
307
459
|
|
|
308
|
-
g.insert(
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
g.insert(
|
|
313
|
-
|
|
314
|
-
|
|
460
|
+
g.insert(
|
|
461
|
+
"parseInt".into(),
|
|
462
|
+
Value::native(|args: &[Value]| globals_builtins::parse_int(args)),
|
|
463
|
+
);
|
|
464
|
+
g.insert(
|
|
465
|
+
"parseFloat".into(),
|
|
466
|
+
Value::native(|args: &[Value]| {
|
|
467
|
+
globals_builtins::parse_float(args)
|
|
468
|
+
}),
|
|
469
|
+
);
|
|
470
|
+
g.insert(
|
|
471
|
+
"encodeURI".into(),
|
|
472
|
+
Value::native(|args: &[Value]| globals_builtins::encode_uri(args)),
|
|
473
|
+
);
|
|
474
|
+
g.insert(
|
|
475
|
+
"decodeURI".into(),
|
|
476
|
+
Value::native(|args: &[Value]| globals_builtins::decode_uri(args)),
|
|
477
|
+
);
|
|
478
|
+
g.insert(
|
|
479
|
+
"htmlEscape".into(),
|
|
480
|
+
Value::native(|args: &[Value]| {
|
|
481
|
+
tishlang_builtins::string::escape_html(args.first().unwrap_or(&Value::Null))
|
|
482
|
+
}),
|
|
483
|
+
);
|
|
484
|
+
g.insert(
|
|
485
|
+
"Boolean".into(),
|
|
486
|
+
Value::native(|args: &[Value]| globals_builtins::boolean(args)),
|
|
487
|
+
);
|
|
488
|
+
g.insert(
|
|
489
|
+
"isFinite".into(),
|
|
490
|
+
Value::native(|args: &[Value]| globals_builtins::is_finite(args)),
|
|
491
|
+
);
|
|
492
|
+
g.insert(
|
|
493
|
+
"isNaN".into(),
|
|
494
|
+
Value::native(|args: &[Value]| globals_builtins::is_nan(args)),
|
|
495
|
+
);
|
|
315
496
|
g.insert("Infinity".into(), Value::Number(f64::INFINITY));
|
|
316
497
|
g.insert("NaN".into(), Value::Number(f64::NAN));
|
|
317
498
|
g.insert(
|
|
318
499
|
"typeof".into(),
|
|
319
|
-
Value::
|
|
500
|
+
Value::native(|args: &[Value]| {
|
|
320
501
|
let v = args.first().unwrap_or(&Value::Null);
|
|
321
502
|
Value::String(v.type_name().into())
|
|
322
|
-
})
|
|
503
|
+
}),
|
|
323
504
|
);
|
|
324
505
|
|
|
325
506
|
// Date - at minimum Date.now() for timing
|
|
326
507
|
let mut date = ObjectMap::default();
|
|
327
508
|
date.insert(
|
|
328
509
|
"now".into(),
|
|
329
|
-
Value::
|
|
510
|
+
Value::native(|_args: &[Value]| {
|
|
330
511
|
let ms = std::time::SystemTime::now()
|
|
331
512
|
.duration_since(std::time::UNIX_EPOCH)
|
|
332
513
|
.unwrap_or_default()
|
|
333
514
|
.as_millis() as f64;
|
|
334
515
|
Value::Number(ms)
|
|
335
|
-
})
|
|
516
|
+
}),
|
|
336
517
|
);
|
|
337
|
-
g.insert("Date".into(), Value::Object(
|
|
518
|
+
g.insert("Date".into(), Value::Object(VmRef::new(date)));
|
|
338
519
|
|
|
339
520
|
g.insert(
|
|
340
521
|
"Uint8Array".into(),
|
|
@@ -349,108 +530,225 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
|
|
|
349
530
|
let mut object_methods = ObjectMap::default();
|
|
350
531
|
object_methods.insert(
|
|
351
532
|
"assign".into(),
|
|
352
|
-
Value::
|
|
533
|
+
Value::native(|args: &[Value]| {
|
|
534
|
+
globals_builtins::object_assign(args)
|
|
535
|
+
}),
|
|
353
536
|
);
|
|
354
537
|
object_methods.insert(
|
|
355
538
|
"fromEntries".into(),
|
|
356
|
-
Value::
|
|
539
|
+
Value::native(|args: &[Value]| {
|
|
540
|
+
globals_builtins::object_from_entries(args)
|
|
541
|
+
}),
|
|
542
|
+
);
|
|
543
|
+
object_methods.insert(
|
|
544
|
+
"keys".into(),
|
|
545
|
+
Value::native(|args: &[Value]| {
|
|
546
|
+
globals_builtins::object_keys(args)
|
|
547
|
+
}),
|
|
548
|
+
);
|
|
549
|
+
object_methods.insert(
|
|
550
|
+
"values".into(),
|
|
551
|
+
Value::native(|args: &[Value]| {
|
|
552
|
+
globals_builtins::object_values(args)
|
|
553
|
+
}),
|
|
554
|
+
);
|
|
555
|
+
object_methods.insert(
|
|
556
|
+
"entries".into(),
|
|
557
|
+
Value::native(|args: &[Value]| {
|
|
558
|
+
globals_builtins::object_entries(args)
|
|
559
|
+
}),
|
|
560
|
+
);
|
|
561
|
+
g.insert(
|
|
562
|
+
"Object".into(),
|
|
563
|
+
Value::Object(VmRef::new(object_methods)),
|
|
357
564
|
);
|
|
358
|
-
object_methods.insert("keys".into(), Value::Function(Rc::new(|args: &[Value]| globals_builtins::object_keys(args))));
|
|
359
|
-
object_methods.insert("values".into(), Value::Function(Rc::new(|args: &[Value]| globals_builtins::object_values(args))));
|
|
360
|
-
object_methods.insert("entries".into(), Value::Function(Rc::new(|args: &[Value]| globals_builtins::object_entries(args))));
|
|
361
|
-
g.insert("Object".into(), Value::Object(Rc::new(RefCell::new(object_methods))));
|
|
362
565
|
|
|
363
566
|
// Array.isArray
|
|
364
567
|
let mut array_static = ObjectMap::default();
|
|
365
568
|
array_static.insert(
|
|
366
569
|
"isArray".into(),
|
|
367
|
-
Value::
|
|
570
|
+
Value::native(|args: &[Value]| {
|
|
571
|
+
globals_builtins::array_is_array(args)
|
|
572
|
+
}),
|
|
573
|
+
);
|
|
574
|
+
g.insert(
|
|
575
|
+
"Array".into(),
|
|
576
|
+
Value::Object(VmRef::new(array_static)),
|
|
368
577
|
);
|
|
369
|
-
g.insert("Array".into(), Value::Object(Rc::new(RefCell::new(array_static))));
|
|
370
578
|
|
|
371
579
|
// String(value) as callable + String.fromCharCode
|
|
372
|
-
let string_convert_fn = Value::
|
|
580
|
+
let string_convert_fn = Value::native(|args: &[Value]| {
|
|
581
|
+
globals_builtins::string_convert(args)
|
|
582
|
+
});
|
|
373
583
|
let mut string_static = ObjectMap::default();
|
|
374
|
-
string_static.insert(
|
|
584
|
+
string_static.insert(
|
|
585
|
+
"fromCharCode".into(),
|
|
586
|
+
Value::native(|args: &[Value]| {
|
|
587
|
+
globals_builtins::string_from_char_code(args)
|
|
588
|
+
}),
|
|
589
|
+
);
|
|
375
590
|
string_static.insert(Arc::from("__call"), string_convert_fn);
|
|
376
|
-
g.insert(
|
|
591
|
+
g.insert(
|
|
592
|
+
"String".into(),
|
|
593
|
+
Value::Object(VmRef::new(string_static)),
|
|
594
|
+
);
|
|
377
595
|
|
|
378
596
|
// JSX / Lattish: stubs for bytecode VM when no DOM (e.g. console). Override via set_global in browser.
|
|
379
597
|
g.insert(
|
|
380
598
|
"h".into(),
|
|
381
|
-
Value::
|
|
599
|
+
Value::native(|_args: &[Value]| Value::Null),
|
|
382
600
|
);
|
|
383
601
|
g.insert(
|
|
384
602
|
"Fragment".into(),
|
|
385
|
-
Value::Object(
|
|
603
|
+
Value::Object(VmRef::new(ObjectMap::default())),
|
|
386
604
|
);
|
|
387
605
|
g.insert(
|
|
388
606
|
"createRoot".into(),
|
|
389
|
-
Value::
|
|
607
|
+
Value::native(|_args: &[Value]| {
|
|
390
608
|
let mut render_obj = ObjectMap::default();
|
|
391
609
|
render_obj.insert(
|
|
392
610
|
"render".into(),
|
|
393
|
-
Value::
|
|
611
|
+
Value::native(|_args: &[Value]| Value::Null),
|
|
394
612
|
);
|
|
395
|
-
Value::Object(
|
|
396
|
-
})
|
|
613
|
+
Value::Object(VmRef::new(render_obj))
|
|
614
|
+
}),
|
|
397
615
|
);
|
|
398
616
|
g.insert(
|
|
399
617
|
"useState".into(),
|
|
400
|
-
Value::
|
|
618
|
+
Value::native(|args: &[Value]| {
|
|
401
619
|
let init = args.first().cloned().unwrap_or(Value::Null);
|
|
402
|
-
let arr = vec![init, Value::
|
|
403
|
-
Value::Array(
|
|
404
|
-
})
|
|
620
|
+
let arr = vec![init, Value::native(|_| Value::Null)];
|
|
621
|
+
Value::Array(VmRef::new(arr))
|
|
622
|
+
}),
|
|
405
623
|
);
|
|
406
624
|
let mut document_obj = ObjectMap::default();
|
|
407
625
|
document_obj.insert("body".into(), Value::Null);
|
|
408
|
-
g.insert(
|
|
626
|
+
g.insert(
|
|
627
|
+
"document".into(),
|
|
628
|
+
Value::Object(VmRef::new(document_obj)),
|
|
629
|
+
);
|
|
409
630
|
|
|
410
631
|
#[cfg(feature = "process")]
|
|
411
632
|
if cap_allows(enabled, "process") {
|
|
412
633
|
let mut process_obj = ObjectMap::default();
|
|
413
634
|
process_obj.insert(
|
|
414
635
|
"exit".into(),
|
|
415
|
-
Value::
|
|
636
|
+
Value::native(|args: &[Value]| {
|
|
637
|
+
tishlang_runtime::process_exit(args)
|
|
638
|
+
}),
|
|
416
639
|
);
|
|
417
640
|
process_obj.insert(
|
|
418
641
|
"cwd".into(),
|
|
419
|
-
Value::
|
|
642
|
+
Value::native(|args: &[Value]| {
|
|
643
|
+
tishlang_runtime::process_cwd(args)
|
|
644
|
+
}),
|
|
420
645
|
);
|
|
421
646
|
process_obj.insert(
|
|
422
647
|
"exec".into(),
|
|
423
|
-
Value::
|
|
648
|
+
Value::native(|args: &[Value]| {
|
|
649
|
+
tishlang_runtime::process_exec(args)
|
|
650
|
+
}),
|
|
424
651
|
);
|
|
425
652
|
process_obj.insert(
|
|
426
653
|
"argv".into(),
|
|
427
|
-
Value::Array(
|
|
654
|
+
Value::Array(VmRef::new(
|
|
428
655
|
std::env::args().map(|s| Value::String(s.into())).collect(),
|
|
429
|
-
))
|
|
656
|
+
)),
|
|
430
657
|
);
|
|
431
658
|
process_obj.insert(
|
|
432
659
|
"env".into(),
|
|
433
|
-
Value::Object(
|
|
660
|
+
Value::Object(VmRef::new(
|
|
434
661
|
std::env::vars()
|
|
435
662
|
.map(|(k, v)| (Arc::from(k.as_str()), Value::String(v.into())))
|
|
436
663
|
.collect(),
|
|
437
|
-
))
|
|
664
|
+
)),
|
|
665
|
+
);
|
|
666
|
+
g.insert(
|
|
667
|
+
"process".into(),
|
|
668
|
+
Value::Object(VmRef::new(process_obj)),
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
#[cfg(feature = "timers")]
|
|
673
|
+
if cap_allows(enabled, "timers") {
|
|
674
|
+
g.insert(
|
|
675
|
+
"setTimeout".into(),
|
|
676
|
+
Value::native(|args: &[Value]| tishlang_runtime::timer_set_timeout(args)),
|
|
677
|
+
);
|
|
678
|
+
g.insert(
|
|
679
|
+
"clearTimeout".into(),
|
|
680
|
+
Value::native(|args: &[Value]| tishlang_runtime::timer_clear_timeout(args)),
|
|
681
|
+
);
|
|
682
|
+
g.insert(
|
|
683
|
+
"setInterval".into(),
|
|
684
|
+
Value::native(|args: &[Value]| tishlang_runtime::timer_set_interval(args)),
|
|
685
|
+
);
|
|
686
|
+
g.insert(
|
|
687
|
+
"clearInterval".into(),
|
|
688
|
+
Value::native(|args: &[Value]| tishlang_runtime::timer_clear_interval(args)),
|
|
438
689
|
);
|
|
439
|
-
g.insert("process".into(), Value::Object(Rc::new(RefCell::new(process_obj))));
|
|
440
690
|
}
|
|
441
691
|
|
|
442
692
|
#[cfg(feature = "http")]
|
|
443
693
|
if cap_allows(enabled, "http") {
|
|
694
|
+
g.insert(
|
|
695
|
+
"fetch".into(),
|
|
696
|
+
Value::native(|args: &[Value]| tishlang_runtime::fetch_promise(args.to_vec())),
|
|
697
|
+
);
|
|
698
|
+
g.insert(
|
|
699
|
+
"fetchAll".into(),
|
|
700
|
+
Value::native(|args: &[Value]| tishlang_runtime::fetch_all_promise(args.to_vec())),
|
|
701
|
+
);
|
|
702
|
+
g.insert("Promise".into(), tishlang_runtime::promise_object());
|
|
703
|
+
g.insert(
|
|
704
|
+
"registerStaticRoute".into(),
|
|
705
|
+
Value::native(|args: &[Value]| {
|
|
706
|
+
let path = match args.first() {
|
|
707
|
+
Some(Value::String(s)) => s.to_string(),
|
|
708
|
+
_ => return Value::Null,
|
|
709
|
+
};
|
|
710
|
+
let body = match args.get(1) {
|
|
711
|
+
Some(Value::String(s)) => s.as_bytes().to_vec(),
|
|
712
|
+
_ => return Value::Null,
|
|
713
|
+
};
|
|
714
|
+
let ct = match args.get(2) {
|
|
715
|
+
Some(Value::String(s)) => s.to_string(),
|
|
716
|
+
_ => "application/octet-stream".to_string(),
|
|
717
|
+
};
|
|
718
|
+
tishlang_runtime::register_static_route(&path, &body, &ct);
|
|
719
|
+
Value::Null
|
|
720
|
+
}),
|
|
721
|
+
);
|
|
444
722
|
g.insert(
|
|
445
723
|
"serve".into(),
|
|
446
|
-
Value::
|
|
447
|
-
|
|
448
|
-
|
|
724
|
+
Value::native(|args: &[Value]| {
|
|
725
|
+
// Phase-1 item 2 (see tish:http.serve above for full docs).
|
|
726
|
+
let raw = args.get(1).cloned().unwrap_or(Value::Null);
|
|
727
|
+
let handler_value = match raw {
|
|
728
|
+
Value::Function(_) => raw,
|
|
729
|
+
Value::Object(ref obj) => {
|
|
730
|
+
let obj_ref = obj.borrow();
|
|
731
|
+
if let Some(Value::Function(on_worker)) =
|
|
732
|
+
obj_ref.get(&std::sync::Arc::from("onWorker")).cloned()
|
|
733
|
+
{
|
|
734
|
+
let args_for_init = [Value::Number(0.0)];
|
|
735
|
+
on_worker(&args_for_init)
|
|
736
|
+
} else if let Some(h) =
|
|
737
|
+
obj_ref.get(&std::sync::Arc::from("handler")).cloned()
|
|
738
|
+
{
|
|
739
|
+
h
|
|
740
|
+
} else {
|
|
741
|
+
Value::Null
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
_ => Value::Null,
|
|
745
|
+
};
|
|
746
|
+
if let Value::Function(f) = handler_value {
|
|
449
747
|
tishlang_runtime::http_serve(args, move |req_args| f(req_args))
|
|
450
748
|
} else {
|
|
451
749
|
Value::Null
|
|
452
750
|
}
|
|
453
|
-
})
|
|
751
|
+
}),
|
|
454
752
|
);
|
|
455
753
|
}
|
|
456
754
|
|
|
@@ -458,7 +756,7 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
|
|
|
458
756
|
}
|
|
459
757
|
|
|
460
758
|
/// Shared scope for closure capture (parent frame's locals).
|
|
461
|
-
type ScopeMap =
|
|
759
|
+
type ScopeMap = VmRef<ObjectMap>;
|
|
462
760
|
|
|
463
761
|
/// Options for the convenience [`run_with_options`] helper (one-shot VM run from the CLI).
|
|
464
762
|
#[derive(Clone, Debug, Default)]
|
|
@@ -475,9 +773,14 @@ pub struct Vm {
|
|
|
475
773
|
scope: ObjectMap,
|
|
476
774
|
/// Enclosing scope for closures (captured parent frame locals).
|
|
477
775
|
enclosing: Option<ScopeMap>,
|
|
478
|
-
globals:
|
|
776
|
+
globals: VmRef<ObjectMap>,
|
|
479
777
|
/// Capabilities for `LoadNativeExport` and globals such as `process` / `serve`.
|
|
480
778
|
capabilities: Arc<HashSet<String>>,
|
|
779
|
+
/// Externally registered native modules, keyed by import spec (e.g.
|
|
780
|
+
/// `"cargo:tish_pg"`). Populated by embedders before `run` (see
|
|
781
|
+
/// [`register_native_module`]). Phase-2 item 11: unblocks `cargo:`
|
|
782
|
+
/// imports on the cranelift and llvm backends which run this VM.
|
|
783
|
+
native_modules: VmRef<HashMap<String, VmRef<ObjectMap>>>,
|
|
481
784
|
}
|
|
482
785
|
|
|
483
786
|
impl Vm {
|
|
@@ -496,11 +799,24 @@ impl Vm {
|
|
|
496
799
|
stack: Vec::new(),
|
|
497
800
|
scope: ObjectMap::default(),
|
|
498
801
|
enclosing: None,
|
|
499
|
-
globals:
|
|
802
|
+
globals: VmRef::new(init_globals(capabilities.as_ref())),
|
|
500
803
|
capabilities,
|
|
804
|
+
native_modules: VmRef::new(HashMap::new()),
|
|
501
805
|
}
|
|
502
806
|
}
|
|
503
807
|
|
|
808
|
+
/// Register an externally-supplied native module under a `cargo:`-style
|
|
809
|
+
/// spec (e.g. `"cargo:tish_pg"`). The `exports` map is what
|
|
810
|
+
/// `LoadNativeExport` will index into when user code imports from this
|
|
811
|
+
/// spec. Intended to be called by the `tishlang_cranelift_runtime` /
|
|
812
|
+
/// `tishlang_llvm` link step, or by external embedders that want to
|
|
813
|
+
/// expose Rust crates to `.tish` programs running on the bytecode VM.
|
|
814
|
+
pub fn register_native_module(&mut self, spec: impl Into<String>, exports: ObjectMap) {
|
|
815
|
+
self.native_modules
|
|
816
|
+
.borrow_mut()
|
|
817
|
+
.insert(spec.into(), VmRef::new(exports));
|
|
818
|
+
}
|
|
819
|
+
|
|
504
820
|
pub fn get_global(&self, name: &str) -> Option<Value> {
|
|
505
821
|
self.globals.borrow().get(name).cloned()
|
|
506
822
|
}
|
|
@@ -511,7 +827,11 @@ impl Vm {
|
|
|
511
827
|
|
|
512
828
|
/// Names of all globals (for REPL bare-word tab completion).
|
|
513
829
|
pub fn global_names(&self) -> Vec<String> {
|
|
514
|
-
self.globals
|
|
830
|
+
self.globals
|
|
831
|
+
.borrow()
|
|
832
|
+
.keys()
|
|
833
|
+
.map(|k| k.as_ref().to_string())
|
|
834
|
+
.collect()
|
|
515
835
|
}
|
|
516
836
|
|
|
517
837
|
fn read_u16(code: &[u8], ip: &mut usize) -> u16 {
|
|
@@ -525,6 +845,22 @@ impl Vm {
|
|
|
525
845
|
Self::read_u16(code, ip) as i16
|
|
526
846
|
}
|
|
527
847
|
|
|
848
|
+
/// Pop innermost try handler, truncate stack, push thrown value, jump to catch.
|
|
849
|
+
fn unwind_throw(
|
|
850
|
+
try_handlers: &mut Vec<(usize, usize)>,
|
|
851
|
+
stack: &mut Vec<Value>,
|
|
852
|
+
ip: &mut usize,
|
|
853
|
+
v: Value,
|
|
854
|
+
) -> Result<(), String> {
|
|
855
|
+
let (catch_ip, stack_len) = try_handlers
|
|
856
|
+
.pop()
|
|
857
|
+
.ok_or_else(|| format!("Uncaught throw: {}", v.to_display_string()))?;
|
|
858
|
+
stack.truncate(stack_len);
|
|
859
|
+
stack.push(v);
|
|
860
|
+
*ip = catch_ip;
|
|
861
|
+
Ok(())
|
|
862
|
+
}
|
|
863
|
+
|
|
528
864
|
pub fn run(&mut self, chunk: &Chunk) -> Result<Value, String> {
|
|
529
865
|
self.run_with_options(chunk, false)
|
|
530
866
|
}
|
|
@@ -546,7 +882,7 @@ impl Vm {
|
|
|
546
882
|
let names = &chunk.names;
|
|
547
883
|
|
|
548
884
|
let mut ip = 0;
|
|
549
|
-
let local_scope: ScopeMap =
|
|
885
|
+
let local_scope: ScopeMap = VmRef::new(ObjectMap::default());
|
|
550
886
|
{
|
|
551
887
|
let mut ls = local_scope.borrow_mut();
|
|
552
888
|
let param_count = chunk.param_count as usize;
|
|
@@ -560,7 +896,7 @@ impl Vm {
|
|
|
560
896
|
let rest_arr: Vec<Value> = args.iter().skip(ri).cloned().collect();
|
|
561
897
|
ls.insert(
|
|
562
898
|
Arc::clone(name),
|
|
563
|
-
Value::Array(
|
|
899
|
+
Value::Array(VmRef::new(rest_arr)),
|
|
564
900
|
);
|
|
565
901
|
}
|
|
566
902
|
}
|
|
@@ -603,20 +939,22 @@ impl Vm {
|
|
|
603
939
|
.get(*nested_idx)
|
|
604
940
|
.ok_or_else(|| "Nested chunk index out of bounds".to_string())?;
|
|
605
941
|
let inner_clone = inner.clone();
|
|
606
|
-
let globals =
|
|
607
|
-
let enclosing = Some(
|
|
942
|
+
let globals = self.globals.clone();
|
|
943
|
+
let enclosing = Some(local_scope.clone());
|
|
608
944
|
let capabilities = Arc::clone(&self.capabilities);
|
|
609
|
-
|
|
945
|
+
let native_modules = self.native_modules.clone();
|
|
946
|
+
Value::native(move |args: &[Value]| {
|
|
610
947
|
let mut vm = Vm {
|
|
611
948
|
stack: Vec::new(),
|
|
612
949
|
scope: ObjectMap::default(),
|
|
613
950
|
enclosing: enclosing.clone(),
|
|
614
|
-
globals:
|
|
951
|
+
globals: globals.clone(),
|
|
615
952
|
capabilities: Arc::clone(&capabilities),
|
|
953
|
+
native_modules: native_modules.clone(),
|
|
616
954
|
};
|
|
617
955
|
vm.run_chunk(&inner_clone, &inner_clone.nested, args, false)
|
|
618
956
|
.unwrap_or(Value::Null)
|
|
619
|
-
})
|
|
957
|
+
})
|
|
620
958
|
}
|
|
621
959
|
};
|
|
622
960
|
self.stack.push(v);
|
|
@@ -663,9 +1001,7 @@ impl Vm {
|
|
|
663
1001
|
} else if self.scope.contains_key(name.as_ref()) {
|
|
664
1002
|
self.scope.insert(Arc::clone(name), v);
|
|
665
1003
|
} else if self.globals.borrow().contains_key(name.as_ref()) {
|
|
666
|
-
self.globals
|
|
667
|
-
.borrow_mut()
|
|
668
|
-
.insert(Arc::clone(name), v);
|
|
1004
|
+
self.globals.borrow_mut().insert(Arc::clone(name), v);
|
|
669
1005
|
} else {
|
|
670
1006
|
// New variable: at top level (no enclosing) store in globals so REPL persists across lines
|
|
671
1007
|
if self.enclosing.is_none() {
|
|
@@ -716,9 +1052,9 @@ impl Vm {
|
|
|
716
1052
|
block_undo_stack.push(Vec::new());
|
|
717
1053
|
}
|
|
718
1054
|
Opcode::ExitBlock => {
|
|
719
|
-
let frame = block_undo_stack
|
|
720
|
-
|
|
721
|
-
|
|
1055
|
+
let frame = block_undo_stack
|
|
1056
|
+
.pop()
|
|
1057
|
+
.ok_or_else(|| "ExitBlock without matching EnterBlock".to_string())?;
|
|
722
1058
|
for (name, old) in frame.into_iter().rev() {
|
|
723
1059
|
let mut ls = local_scope.borrow_mut();
|
|
724
1060
|
match old {
|
|
@@ -753,17 +1089,19 @@ impl Vm {
|
|
|
753
1089
|
.stack
|
|
754
1090
|
.pop()
|
|
755
1091
|
.ok_or_else(|| "Stack underflow".to_string())?;
|
|
756
|
-
self.globals
|
|
757
|
-
.borrow_mut()
|
|
758
|
-
.insert(Arc::clone(name), v);
|
|
1092
|
+
self.globals.borrow_mut().insert(Arc::clone(name), v);
|
|
759
1093
|
}
|
|
760
1094
|
Opcode::Pop => {
|
|
761
|
-
self.stack
|
|
1095
|
+
self.stack
|
|
1096
|
+
.pop()
|
|
1097
|
+
.ok_or_else(|| "Stack underflow".to_string())?;
|
|
762
1098
|
}
|
|
763
1099
|
Opcode::PopN => {
|
|
764
1100
|
let n = Self::read_u16(code, &mut ip) as usize;
|
|
765
1101
|
for _ in 0..n {
|
|
766
|
-
self.stack
|
|
1102
|
+
self.stack
|
|
1103
|
+
.pop()
|
|
1104
|
+
.ok_or_else(|| "Stack underflow".to_string())?;
|
|
767
1105
|
}
|
|
768
1106
|
}
|
|
769
1107
|
Opcode::Dup => {
|
|
@@ -790,10 +1128,12 @@ impl Vm {
|
|
|
790
1128
|
.pop()
|
|
791
1129
|
.ok_or_else(|| "Stack underflow: no callee".to_string())?;
|
|
792
1130
|
let f = match &callee {
|
|
793
|
-
Value::Function(f) =>
|
|
1131
|
+
Value::Function(f) => f.clone(),
|
|
794
1132
|
Value::Object(o) => {
|
|
795
|
-
if let Some(Value::Function(call_fn)) =
|
|
796
|
-
|
|
1133
|
+
if let Some(Value::Function(call_fn)) =
|
|
1134
|
+
o.borrow().get(&Arc::from("__call"))
|
|
1135
|
+
{
|
|
1136
|
+
call_fn.clone()
|
|
797
1137
|
} else {
|
|
798
1138
|
return Err(format!(
|
|
799
1139
|
"Call of non-function: {}",
|
|
@@ -802,10 +1142,7 @@ impl Vm {
|
|
|
802
1142
|
}
|
|
803
1143
|
}
|
|
804
1144
|
_ => {
|
|
805
|
-
return Err(format!(
|
|
806
|
-
"Call of non-function: {}",
|
|
807
|
-
callee.type_name()
|
|
808
|
-
));
|
|
1145
|
+
return Err(format!("Call of non-function: {}", callee.type_name()));
|
|
809
1146
|
}
|
|
810
1147
|
};
|
|
811
1148
|
let result = f(&args);
|
|
@@ -830,10 +1167,12 @@ impl Vm {
|
|
|
830
1167
|
}
|
|
831
1168
|
};
|
|
832
1169
|
let f = match &callee {
|
|
833
|
-
Value::Function(f) =>
|
|
1170
|
+
Value::Function(f) => f.clone(),
|
|
834
1171
|
Value::Object(o) => {
|
|
835
|
-
if let Some(Value::Function(call_fn)) =
|
|
836
|
-
|
|
1172
|
+
if let Some(Value::Function(call_fn)) =
|
|
1173
|
+
o.borrow().get(&Arc::from("__call"))
|
|
1174
|
+
{
|
|
1175
|
+
call_fn.clone()
|
|
837
1176
|
} else {
|
|
838
1177
|
return Err(format!(
|
|
839
1178
|
"Call of non-function: {}",
|
|
@@ -842,10 +1181,7 @@ impl Vm {
|
|
|
842
1181
|
}
|
|
843
1182
|
}
|
|
844
1183
|
_ => {
|
|
845
|
-
return Err(format!(
|
|
846
|
-
"Call of non-function: {}",
|
|
847
|
-
callee.type_name()
|
|
848
|
-
));
|
|
1184
|
+
return Err(format!("Call of non-function: {}", callee.type_name()));
|
|
849
1185
|
}
|
|
850
1186
|
};
|
|
851
1187
|
let result = f(&args);
|
|
@@ -922,8 +1258,8 @@ impl Vm {
|
|
|
922
1258
|
.stack
|
|
923
1259
|
.pop()
|
|
924
1260
|
.ok_or_else(|| "Stack underflow".to_string())?;
|
|
925
|
-
let op =
|
|
926
|
-
.ok_or_else(|| format!("Unknown binop: {}", op_u8))?;
|
|
1261
|
+
let op =
|
|
1262
|
+
u8_to_binop(op_u8).ok_or_else(|| format!("Unknown binop: {}", op_u8))?;
|
|
927
1263
|
let result = eval_binop(op, &l, &r)?;
|
|
928
1264
|
self.stack.push(result);
|
|
929
1265
|
}
|
|
@@ -1023,8 +1359,7 @@ impl Vm {
|
|
|
1023
1359
|
);
|
|
1024
1360
|
}
|
|
1025
1361
|
elems.reverse();
|
|
1026
|
-
self.stack
|
|
1027
|
-
.push(Value::Array(Rc::new(RefCell::new(elems))));
|
|
1362
|
+
self.stack.push(Value::Array(VmRef::new(elems)));
|
|
1028
1363
|
}
|
|
1029
1364
|
Opcode::NewObject => {
|
|
1030
1365
|
let n = Self::read_u16(code, &mut ip) as usize;
|
|
@@ -1041,8 +1376,7 @@ impl Vm {
|
|
|
1041
1376
|
let key = key_val.to_display_string().into();
|
|
1042
1377
|
map.insert(key, val);
|
|
1043
1378
|
}
|
|
1044
|
-
self.stack
|
|
1045
|
-
.push(Value::Object(Rc::new(RefCell::new(map))));
|
|
1379
|
+
self.stack.push(Value::Object(VmRef::new(map)));
|
|
1046
1380
|
}
|
|
1047
1381
|
Opcode::EnterTry => {
|
|
1048
1382
|
let offset = Self::read_u16(code, &mut ip) as usize;
|
|
@@ -1082,7 +1416,7 @@ impl Vm {
|
|
|
1082
1416
|
},
|
|
1083
1417
|
);
|
|
1084
1418
|
a.extend(b);
|
|
1085
|
-
self.stack.push(Value::Array(
|
|
1419
|
+
self.stack.push(Value::Array(VmRef::new(a)));
|
|
1086
1420
|
}
|
|
1087
1421
|
Opcode::MergeObject => {
|
|
1088
1422
|
let right = self
|
|
@@ -1117,7 +1451,7 @@ impl Vm {
|
|
|
1117
1451
|
));
|
|
1118
1452
|
}
|
|
1119
1453
|
self.stack
|
|
1120
|
-
.push(Value::Object(
|
|
1454
|
+
.push(Value::Object(VmRef::new(merged)));
|
|
1121
1455
|
}
|
|
1122
1456
|
Opcode::ArraySortNumeric => {
|
|
1123
1457
|
let operand = Self::read_u16(code, &mut ip);
|
|
@@ -1159,9 +1493,7 @@ impl Vm {
|
|
|
1159
1493
|
.pop()
|
|
1160
1494
|
.ok_or_else(|| "Stack underflow".to_string())?;
|
|
1161
1495
|
let result = match &arr {
|
|
1162
|
-
Value::Array(a) =>
|
|
1163
|
-
Value::Array(Rc::new(RefCell::new(a.borrow().clone())))
|
|
1164
|
-
}
|
|
1496
|
+
Value::Array(a) => Value::Array(VmRef::new(a.borrow().clone())),
|
|
1165
1497
|
_ => Value::Null,
|
|
1166
1498
|
};
|
|
1167
1499
|
self.stack.push(result);
|
|
@@ -1187,12 +1519,20 @@ impl Vm {
|
|
|
1187
1519
|
let mapped: Vec<Value> = arr_borrow
|
|
1188
1520
|
.iter()
|
|
1189
1521
|
.map(|v| {
|
|
1190
|
-
let l: Value = if param_left {
|
|
1191
|
-
|
|
1522
|
+
let l: Value = if param_left {
|
|
1523
|
+
(*v).clone()
|
|
1524
|
+
} else {
|
|
1525
|
+
const_val.clone()
|
|
1526
|
+
};
|
|
1527
|
+
let r: Value = if param_left {
|
|
1528
|
+
const_val.clone()
|
|
1529
|
+
} else {
|
|
1530
|
+
(*v).clone()
|
|
1531
|
+
};
|
|
1192
1532
|
eval_binop(binop, &l, &r).unwrap_or(Value::Null)
|
|
1193
1533
|
})
|
|
1194
1534
|
.collect();
|
|
1195
|
-
Value::Array(
|
|
1535
|
+
Value::Array(VmRef::new(mapped))
|
|
1196
1536
|
} else {
|
|
1197
1537
|
Value::Null
|
|
1198
1538
|
};
|
|
@@ -1204,8 +1544,9 @@ impl Vm {
|
|
|
1204
1544
|
let const_idx = Self::read_u16(code, &mut ip);
|
|
1205
1545
|
let param_left = code[ip] == 0; // 0 = param on left (x op const), 1 = param on right (const op x)
|
|
1206
1546
|
ip += 1;
|
|
1207
|
-
let binop = u8_to_binop(binop_u8)
|
|
1208
|
-
|
|
1547
|
+
let binop = u8_to_binop(binop_u8).ok_or_else(|| {
|
|
1548
|
+
format!("Unknown binop in ArrayFilterBinOp: {}", binop_u8)
|
|
1549
|
+
})?;
|
|
1209
1550
|
let arr = self
|
|
1210
1551
|
.stack
|
|
1211
1552
|
.pop()
|
|
@@ -1219,14 +1560,22 @@ impl Vm {
|
|
|
1219
1560
|
let filtered: Vec<Value> = arr_borrow
|
|
1220
1561
|
.iter()
|
|
1221
1562
|
.filter(|v| {
|
|
1222
|
-
let l: Value = if param_left {
|
|
1223
|
-
|
|
1563
|
+
let l: Value = if param_left {
|
|
1564
|
+
(*v).clone()
|
|
1565
|
+
} else {
|
|
1566
|
+
const_val.clone()
|
|
1567
|
+
};
|
|
1568
|
+
let r: Value = if param_left {
|
|
1569
|
+
const_val.clone()
|
|
1570
|
+
} else {
|
|
1571
|
+
(*v).clone()
|
|
1572
|
+
};
|
|
1224
1573
|
let b = eval_binop(binop, &l, &r).unwrap_or(Value::Null);
|
|
1225
1574
|
b.is_truthy()
|
|
1226
1575
|
})
|
|
1227
1576
|
.cloned()
|
|
1228
1577
|
.collect();
|
|
1229
|
-
Value::Array(
|
|
1578
|
+
Value::Array(VmRef::new(filtered))
|
|
1230
1579
|
} else {
|
|
1231
1580
|
Value::Null
|
|
1232
1581
|
};
|
|
@@ -1237,12 +1586,30 @@ impl Vm {
|
|
|
1237
1586
|
.stack
|
|
1238
1587
|
.pop()
|
|
1239
1588
|
.ok_or_else(|| "Stack underflow".to_string())?;
|
|
1240
|
-
|
|
1589
|
+
Self::unwind_throw(&mut try_handlers, &mut self.stack, &mut ip, v)?;
|
|
1590
|
+
}
|
|
1591
|
+
Opcode::AwaitPromise => {
|
|
1592
|
+
let v = self
|
|
1593
|
+
.stack
|
|
1241
1594
|
.pop()
|
|
1242
|
-
.ok_or_else(||
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1595
|
+
.ok_or_else(|| "Stack underflow in AwaitPromise".to_string())?;
|
|
1596
|
+
#[cfg(feature = "http")]
|
|
1597
|
+
{
|
|
1598
|
+
use tishlang_core::Value as V;
|
|
1599
|
+
match v {
|
|
1600
|
+
V::Promise(p) => match p.block_until_settled() {
|
|
1601
|
+
Ok(val) => self.stack.push(val),
|
|
1602
|
+
Err(rej) => {
|
|
1603
|
+
Self::unwind_throw(&mut try_handlers, &mut self.stack, &mut ip, rej)?;
|
|
1604
|
+
}
|
|
1605
|
+
},
|
|
1606
|
+
other => self.stack.push(tishlang_runtime::await_promise(other)),
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
#[cfg(not(feature = "http"))]
|
|
1610
|
+
{
|
|
1611
|
+
self.stack.push(v);
|
|
1612
|
+
}
|
|
1246
1613
|
}
|
|
1247
1614
|
Opcode::LoadNativeExport => {
|
|
1248
1615
|
let spec_idx = Self::read_u16(code, &mut ip);
|
|
@@ -1250,7 +1617,10 @@ impl Vm {
|
|
|
1250
1617
|
let spec = match constants.get(spec_idx as usize) {
|
|
1251
1618
|
Some(Constant::String(s)) => s.as_ref(),
|
|
1252
1619
|
_ => {
|
|
1253
|
-
return Err(
|
|
1620
|
+
return Err(
|
|
1621
|
+
"LoadNativeExport: spec constant out of bounds or not string"
|
|
1622
|
+
.to_string(),
|
|
1623
|
+
);
|
|
1254
1624
|
}
|
|
1255
1625
|
};
|
|
1256
1626
|
let export_name = match constants.get(export_idx as usize) {
|
|
@@ -1259,19 +1629,36 @@ impl Vm {
|
|
|
1259
1629
|
return Err("LoadNativeExport: export_name constant out of bounds or not string".to_string());
|
|
1260
1630
|
}
|
|
1261
1631
|
};
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
}
|
|
1632
|
+
// Phase-2 item 11: consult externally registered native
|
|
1633
|
+
// modules (populated via `Vm::register_native_module`)
|
|
1634
|
+
// before falling through to the built-in lookup. Embedders
|
|
1635
|
+
// on the cranelift / llvm backends that want to expose
|
|
1636
|
+
// `cargo:…` Rust crates should register the module's
|
|
1637
|
+
// exports map before calling `vm.run(chunk)`.
|
|
1638
|
+
let from_registry: Option<Value> = if spec.starts_with("cargo:") {
|
|
1639
|
+
let regs = self.native_modules.borrow();
|
|
1640
|
+
regs.get(spec)
|
|
1641
|
+
.and_then(|m| m.borrow().get(&Arc::from(export_name)).cloned())
|
|
1642
|
+
} else {
|
|
1643
|
+
None
|
|
1644
|
+
};
|
|
1645
|
+
let v = from_registry
|
|
1646
|
+
.or_else(|| get_builtin_export(self.capabilities.as_ref(), spec, export_name))
|
|
1647
|
+
.ok_or_else(|| {
|
|
1648
|
+
if spec.starts_with("cargo:") {
|
|
1649
|
+
format!(
|
|
1650
|
+
"cargo:{} is not registered on the bytecode VM. Embedders must call Vm::register_native_module before run(). Spec: {} export: {}",
|
|
1651
|
+
spec.trim_start_matches("cargo:"),
|
|
1652
|
+
spec,
|
|
1653
|
+
export_name,
|
|
1654
|
+
)
|
|
1655
|
+
} else {
|
|
1656
|
+
format!(
|
|
1657
|
+
"Built-in module '{}' does not export '{}' or capability not enabled for this run. Use e.g. tish run --feature fs (or full). The tish binary must also be built with that capability linked in.",
|
|
1658
|
+
spec, export_name
|
|
1659
|
+
)
|
|
1660
|
+
}
|
|
1661
|
+
})?;
|
|
1275
1662
|
self.stack.push(v);
|
|
1276
1663
|
}
|
|
1277
1664
|
Opcode::Closure | Opcode::LoadThis => {
|
|
@@ -1280,6 +1667,11 @@ impl Vm {
|
|
|
1280
1667
|
}
|
|
1281
1668
|
}
|
|
1282
1669
|
|
|
1670
|
+
#[cfg(feature = "timers")]
|
|
1671
|
+
if cap_allows(self.capabilities.as_ref(), "timers") {
|
|
1672
|
+
tishlang_runtime::drain_timers();
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1283
1675
|
Ok(self.stack.pop().unwrap_or(Value::Null))
|
|
1284
1676
|
}
|
|
1285
1677
|
}
|
|
@@ -1400,100 +1792,125 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
|
|
|
1400
1792
|
match obj {
|
|
1401
1793
|
Value::Object(m) => {
|
|
1402
1794
|
let map = m.borrow();
|
|
1403
|
-
map.get(key.as_ref())
|
|
1404
|
-
|
|
1405
|
-
|
|
1795
|
+
map.get(key.as_ref())
|
|
1796
|
+
.cloned()
|
|
1797
|
+
.ok_or_else(|| format!("Property '{}' not found", key))
|
|
1406
1798
|
}
|
|
1407
1799
|
Value::Array(a) => {
|
|
1408
1800
|
let key_s = key.as_ref();
|
|
1409
1801
|
if let Ok(idx) = key_s.parse::<usize>() {
|
|
1410
1802
|
let arr = a.borrow();
|
|
1411
|
-
return arr
|
|
1803
|
+
return arr
|
|
1804
|
+
.get(idx)
|
|
1805
|
+
.cloned()
|
|
1806
|
+
.ok_or_else(|| "Index out of bounds".to_string());
|
|
1412
1807
|
}
|
|
1413
1808
|
if key_s == "length" {
|
|
1414
1809
|
return Ok(Value::Number(a.borrow().len() as f64));
|
|
1415
1810
|
}
|
|
1416
|
-
let a_clone =
|
|
1811
|
+
let a_clone = a.clone();
|
|
1417
1812
|
let method: ArrayMethodFn = match key_s {
|
|
1418
|
-
"push" =>
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
"
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
"
|
|
1813
|
+
"push" => make_native_fn(move |args: &[Value]| {
|
|
1814
|
+
arr_builtins::push(&Value::Array(a_clone.clone()), args)
|
|
1815
|
+
}),
|
|
1816
|
+
"pop" => make_native_fn(move |_args: &[Value]| {
|
|
1817
|
+
arr_builtins::pop(&Value::Array(a_clone.clone()))
|
|
1818
|
+
}),
|
|
1819
|
+
"shift" => make_native_fn(move |_args: &[Value]| {
|
|
1820
|
+
arr_builtins::shift(&Value::Array(a_clone.clone()))
|
|
1821
|
+
}),
|
|
1822
|
+
"unshift" => make_native_fn(move |args: &[Value]| {
|
|
1823
|
+
arr_builtins::unshift(&Value::Array(a_clone.clone()), args)
|
|
1824
|
+
}),
|
|
1825
|
+
"reverse" => make_native_fn(move |_args: &[Value]| {
|
|
1826
|
+
arr_builtins::reverse(&Value::Array(a_clone.clone()))
|
|
1827
|
+
}),
|
|
1828
|
+
"shuffle" => make_native_fn(move |_args: &[Value]| {
|
|
1829
|
+
arr_builtins::shuffle(&Value::Array(a_clone.clone()))
|
|
1830
|
+
}),
|
|
1831
|
+
"slice" => make_native_fn(move |args: &[Value]| {
|
|
1425
1832
|
let start = args.first().unwrap_or(&Value::Null);
|
|
1426
1833
|
let end = args.get(1).unwrap_or(&Value::Null);
|
|
1427
|
-
arr_builtins::slice(&Value::Array(
|
|
1834
|
+
arr_builtins::slice(&Value::Array(a_clone.clone()), start, end)
|
|
1835
|
+
}),
|
|
1836
|
+
"concat" => make_native_fn(move |args: &[Value]| {
|
|
1837
|
+
arr_builtins::concat(&Value::Array(a_clone.clone()), args)
|
|
1428
1838
|
}),
|
|
1429
|
-
"
|
|
1430
|
-
"join" => Rc::new(move |args: &[Value]| {
|
|
1839
|
+
"join" => make_native_fn(move |args: &[Value]| {
|
|
1431
1840
|
let sep = args.first().unwrap_or(&Value::Null);
|
|
1432
|
-
arr_builtins::join(&Value::Array(
|
|
1841
|
+
arr_builtins::join(&Value::Array(a_clone.clone()), sep)
|
|
1433
1842
|
}),
|
|
1434
|
-
"indexOf" =>
|
|
1843
|
+
"indexOf" => make_native_fn(move |args: &[Value]| {
|
|
1435
1844
|
let search = args.first().unwrap_or(&Value::Null);
|
|
1436
|
-
arr_builtins::index_of(&Value::Array(
|
|
1845
|
+
arr_builtins::index_of(&Value::Array(a_clone.clone()), search)
|
|
1437
1846
|
}),
|
|
1438
|
-
"includes" =>
|
|
1847
|
+
"includes" => make_native_fn(move |args: &[Value]| {
|
|
1439
1848
|
let search = args.first().unwrap_or(&Value::Null);
|
|
1440
1849
|
let from = args.get(1);
|
|
1441
|
-
arr_builtins::includes(&Value::Array(
|
|
1850
|
+
arr_builtins::includes(&Value::Array(a_clone.clone()), search, from)
|
|
1442
1851
|
}),
|
|
1443
|
-
"map" =>
|
|
1852
|
+
"map" => make_native_fn(move |args: &[Value]| {
|
|
1444
1853
|
let cb = args.first().cloned().unwrap_or(Value::Null);
|
|
1445
|
-
arr_builtins::map(&Value::Array(
|
|
1854
|
+
arr_builtins::map(&Value::Array(a_clone.clone()), &cb)
|
|
1446
1855
|
}),
|
|
1447
|
-
"filter" =>
|
|
1856
|
+
"filter" => make_native_fn(move |args: &[Value]| {
|
|
1448
1857
|
let cb = args.first().cloned().unwrap_or(Value::Null);
|
|
1449
|
-
arr_builtins::filter(&Value::Array(
|
|
1858
|
+
arr_builtins::filter(&Value::Array(a_clone.clone()), &cb)
|
|
1450
1859
|
}),
|
|
1451
|
-
"reduce" =>
|
|
1860
|
+
"reduce" => make_native_fn(move |args: &[Value]| {
|
|
1452
1861
|
let cb = args.first().cloned().unwrap_or(Value::Null);
|
|
1453
1862
|
let init = args.get(1).cloned().unwrap_or(Value::Null);
|
|
1454
|
-
arr_builtins::reduce(&Value::Array(
|
|
1863
|
+
arr_builtins::reduce(&Value::Array(a_clone.clone()), &cb, &init)
|
|
1455
1864
|
}),
|
|
1456
|
-
"forEach" =>
|
|
1865
|
+
"forEach" => make_native_fn(move |args: &[Value]| {
|
|
1457
1866
|
let cb = args.first().cloned().unwrap_or(Value::Null);
|
|
1458
|
-
arr_builtins::for_each(&Value::Array(
|
|
1867
|
+
arr_builtins::for_each(&Value::Array(a_clone.clone()), &cb)
|
|
1459
1868
|
}),
|
|
1460
|
-
"find" =>
|
|
1869
|
+
"find" => make_native_fn(move |args: &[Value]| {
|
|
1461
1870
|
let cb = args.first().cloned().unwrap_or(Value::Null);
|
|
1462
|
-
arr_builtins::find(&Value::Array(
|
|
1871
|
+
arr_builtins::find(&Value::Array(a_clone.clone()), &cb)
|
|
1463
1872
|
}),
|
|
1464
|
-
"findIndex" =>
|
|
1873
|
+
"findIndex" => make_native_fn(move |args: &[Value]| {
|
|
1465
1874
|
let cb = args.first().cloned().unwrap_or(Value::Null);
|
|
1466
|
-
arr_builtins::find_index(&Value::Array(
|
|
1875
|
+
arr_builtins::find_index(&Value::Array(a_clone.clone()), &cb)
|
|
1467
1876
|
}),
|
|
1468
|
-
"some" =>
|
|
1877
|
+
"some" => make_native_fn(move |args: &[Value]| {
|
|
1469
1878
|
let cb = args.first().cloned().unwrap_or(Value::Null);
|
|
1470
|
-
arr_builtins::some(&Value::Array(
|
|
1879
|
+
arr_builtins::some(&Value::Array(a_clone.clone()), &cb)
|
|
1471
1880
|
}),
|
|
1472
|
-
"every" =>
|
|
1881
|
+
"every" => make_native_fn(move |args: &[Value]| {
|
|
1473
1882
|
let cb = args.first().cloned().unwrap_or(Value::Null);
|
|
1474
|
-
arr_builtins::every(&Value::Array(
|
|
1883
|
+
arr_builtins::every(&Value::Array(a_clone.clone()), &cb)
|
|
1475
1884
|
}),
|
|
1476
|
-
"flat" =>
|
|
1885
|
+
"flat" => make_native_fn(move |args: &[Value]| {
|
|
1477
1886
|
let depth = args.first().unwrap_or(&Value::Number(1.0));
|
|
1478
|
-
arr_builtins::flat(&Value::Array(
|
|
1887
|
+
arr_builtins::flat(&Value::Array(a_clone.clone()), depth)
|
|
1479
1888
|
}),
|
|
1480
|
-
"flatMap" =>
|
|
1889
|
+
"flatMap" => make_native_fn(move |args: &[Value]| {
|
|
1481
1890
|
let cb = args.first().cloned().unwrap_or(Value::Null);
|
|
1482
|
-
arr_builtins::flat_map(&Value::Array(
|
|
1891
|
+
arr_builtins::flat_map(&Value::Array(a_clone.clone()), &cb)
|
|
1483
1892
|
}),
|
|
1484
|
-
"sort" =>
|
|
1893
|
+
"sort" => make_native_fn(move |args: &[Value]| {
|
|
1485
1894
|
let cmp = args.first();
|
|
1486
1895
|
if let Some(Value::Function(_)) = cmp {
|
|
1487
|
-
arr_builtins::sort_with_comparator(
|
|
1896
|
+
arr_builtins::sort_with_comparator(
|
|
1897
|
+
&Value::Array(a_clone.clone()),
|
|
1898
|
+
cmp.unwrap(),
|
|
1899
|
+
)
|
|
1488
1900
|
} else {
|
|
1489
|
-
arr_builtins::sort_default(&Value::Array(
|
|
1901
|
+
arr_builtins::sort_default(&Value::Array(a_clone.clone()))
|
|
1490
1902
|
}
|
|
1491
1903
|
}),
|
|
1492
|
-
"splice" =>
|
|
1904
|
+
"splice" => make_native_fn(move |args: &[Value]| {
|
|
1493
1905
|
let start = args.first().unwrap_or(&Value::Null);
|
|
1494
1906
|
let delete_count = args.get(1).map(|v| v as &Value);
|
|
1495
1907
|
let items: Vec<Value> = args.get(2..).unwrap_or(&[]).to_vec();
|
|
1496
|
-
arr_builtins::splice(
|
|
1908
|
+
arr_builtins::splice(
|
|
1909
|
+
&Value::Array(a_clone.clone()),
|
|
1910
|
+
start,
|
|
1911
|
+
delete_count,
|
|
1912
|
+
&items,
|
|
1913
|
+
)
|
|
1497
1914
|
}),
|
|
1498
1915
|
_ => return Err(format!("Property '{}' not found", key)),
|
|
1499
1916
|
};
|
|
@@ -1512,81 +1929,88 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
|
|
|
1512
1929
|
}
|
|
1513
1930
|
let s_clone: Arc<str> = Arc::clone(s);
|
|
1514
1931
|
let method: ArrayMethodFn = match key_s {
|
|
1515
|
-
"indexOf" =>
|
|
1932
|
+
"indexOf" => make_native_fn(move |args: &[Value]| {
|
|
1516
1933
|
let search = args.first().unwrap_or(&Value::Null);
|
|
1517
1934
|
let from = args.get(1);
|
|
1518
1935
|
str_builtins::index_of(&Value::String(Arc::clone(&s_clone)), search, from)
|
|
1519
1936
|
}),
|
|
1520
|
-
"lastIndexOf" =>
|
|
1937
|
+
"lastIndexOf" => make_native_fn(move |args: &[Value]| {
|
|
1521
1938
|
let search = args.first().unwrap_or(&Value::Null);
|
|
1522
|
-
let position = args
|
|
1523
|
-
.get(1)
|
|
1524
|
-
.cloned()
|
|
1525
|
-
.unwrap_or(Value::Number(f64::INFINITY));
|
|
1939
|
+
let position = args.get(1).cloned().unwrap_or(Value::Number(f64::INFINITY));
|
|
1526
1940
|
str_builtins::last_index_of(
|
|
1527
1941
|
&Value::String(Arc::clone(&s_clone)),
|
|
1528
1942
|
search,
|
|
1529
1943
|
&position,
|
|
1530
1944
|
)
|
|
1531
1945
|
}),
|
|
1532
|
-
"includes" =>
|
|
1946
|
+
"includes" => make_native_fn(move |args: &[Value]| {
|
|
1533
1947
|
let search = args.first().unwrap_or(&Value::Null);
|
|
1534
1948
|
let from = args.get(1);
|
|
1535
1949
|
str_builtins::includes(&Value::String(Arc::clone(&s_clone)), search, from)
|
|
1536
1950
|
}),
|
|
1537
|
-
"slice" =>
|
|
1951
|
+
"slice" => make_native_fn(move |args: &[Value]| {
|
|
1538
1952
|
let start = args.first().unwrap_or(&Value::Null);
|
|
1539
1953
|
let end = args.get(1).unwrap_or(&Value::Null);
|
|
1540
1954
|
str_builtins::slice(&Value::String(Arc::clone(&s_clone)), start, end)
|
|
1541
1955
|
}),
|
|
1542
|
-
"substring" =>
|
|
1956
|
+
"substring" => make_native_fn(move |args: &[Value]| {
|
|
1543
1957
|
let start = args.first().unwrap_or(&Value::Null);
|
|
1544
1958
|
let end = args.get(1).unwrap_or(&Value::Null);
|
|
1545
1959
|
str_builtins::substring(&Value::String(Arc::clone(&s_clone)), start, end)
|
|
1546
1960
|
}),
|
|
1547
|
-
"split" =>
|
|
1961
|
+
"split" => make_native_fn(move |args: &[Value]| {
|
|
1548
1962
|
let sep = args.first().unwrap_or(&Value::Null);
|
|
1549
1963
|
str_builtins::split(&Value::String(Arc::clone(&s_clone)), sep)
|
|
1550
1964
|
}),
|
|
1551
|
-
"trim" =>
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
"
|
|
1965
|
+
"trim" => make_native_fn(move |_args: &[Value]| {
|
|
1966
|
+
str_builtins::trim(&Value::String(Arc::clone(&s_clone)))
|
|
1967
|
+
}),
|
|
1968
|
+
"toUpperCase" => make_native_fn(move |_args: &[Value]| {
|
|
1969
|
+
str_builtins::to_upper_case(&Value::String(Arc::clone(&s_clone)))
|
|
1970
|
+
}),
|
|
1971
|
+
"toLowerCase" => make_native_fn(move |_args: &[Value]| {
|
|
1972
|
+
str_builtins::to_lower_case(&Value::String(Arc::clone(&s_clone)))
|
|
1973
|
+
}),
|
|
1974
|
+
"startsWith" => make_native_fn(move |args: &[Value]| {
|
|
1555
1975
|
let search = args.first().unwrap_or(&Value::Null);
|
|
1556
1976
|
str_builtins::starts_with(&Value::String(Arc::clone(&s_clone)), search)
|
|
1557
1977
|
}),
|
|
1558
|
-
"endsWith" =>
|
|
1978
|
+
"endsWith" => make_native_fn(move |args: &[Value]| {
|
|
1559
1979
|
let search = args.first().unwrap_or(&Value::Null);
|
|
1560
1980
|
str_builtins::ends_with(&Value::String(Arc::clone(&s_clone)), search)
|
|
1561
1981
|
}),
|
|
1562
|
-
"replace" =>
|
|
1982
|
+
"replace" => make_native_fn(move |args: &[Value]| {
|
|
1563
1983
|
let search = args.first().unwrap_or(&Value::Null);
|
|
1564
1984
|
let replacement = args.get(1).unwrap_or(&Value::Null);
|
|
1565
1985
|
str_builtins::replace(&Value::String(Arc::clone(&s_clone)), search, replacement)
|
|
1566
1986
|
}),
|
|
1567
|
-
"replaceAll" =>
|
|
1987
|
+
"replaceAll" => make_native_fn(move |args: &[Value]| {
|
|
1568
1988
|
let search = args.first().unwrap_or(&Value::Null);
|
|
1569
1989
|
let replacement = args.get(1).unwrap_or(&Value::Null);
|
|
1570
|
-
str_builtins::replace_all(
|
|
1990
|
+
str_builtins::replace_all(
|
|
1991
|
+
&Value::String(Arc::clone(&s_clone)),
|
|
1992
|
+
search,
|
|
1993
|
+
replacement,
|
|
1994
|
+
)
|
|
1571
1995
|
}),
|
|
1572
|
-
"charAt" =>
|
|
1996
|
+
"charAt" => make_native_fn(move |args: &[Value]| {
|
|
1573
1997
|
let idx = args.first().unwrap_or(&Value::Null);
|
|
1574
1998
|
str_builtins::char_at(&Value::String(Arc::clone(&s_clone)), idx)
|
|
1575
1999
|
}),
|
|
1576
|
-
"charCodeAt" =>
|
|
2000
|
+
"charCodeAt" => make_native_fn(move |args: &[Value]| {
|
|
1577
2001
|
let idx = args.first().unwrap_or(&Value::Null);
|
|
1578
2002
|
str_builtins::char_code_at(&Value::String(Arc::clone(&s_clone)), idx)
|
|
1579
2003
|
}),
|
|
1580
|
-
"repeat" =>
|
|
2004
|
+
"repeat" => make_native_fn(move |args: &[Value]| {
|
|
1581
2005
|
let count = args.first().unwrap_or(&Value::Null);
|
|
1582
2006
|
str_builtins::repeat(&Value::String(Arc::clone(&s_clone)), count)
|
|
1583
2007
|
}),
|
|
1584
|
-
"padStart" =>
|
|
2008
|
+
"padStart" => make_native_fn(move |args: &[Value]| {
|
|
1585
2009
|
let target_len = args.first().unwrap_or(&Value::Null);
|
|
1586
2010
|
let pad = args.get(1).unwrap_or(&Value::Null);
|
|
1587
2011
|
str_builtins::pad_start(&Value::String(Arc::clone(&s_clone)), target_len, pad)
|
|
1588
2012
|
}),
|
|
1589
|
-
"padEnd" =>
|
|
2013
|
+
"padEnd" => make_native_fn(move |args: &[Value]| {
|
|
1590
2014
|
let target_len = args.first().unwrap_or(&Value::Null);
|
|
1591
2015
|
let pad = args.get(1).unwrap_or(&Value::Null);
|
|
1592
2016
|
str_builtins::pad_end(&Value::String(Arc::clone(&s_clone)), target_len, pad)
|
|
@@ -1595,7 +2019,27 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
|
|
|
1595
2019
|
};
|
|
1596
2020
|
Ok(Value::Function(method))
|
|
1597
2021
|
}
|
|
1598
|
-
|
|
2022
|
+
#[cfg(feature = "http")]
|
|
2023
|
+
Value::Promise(p) => match key.as_ref() {
|
|
2024
|
+
"then" => {
|
|
2025
|
+
let pc = Arc::clone(p);
|
|
2026
|
+
Ok(Value::native(move |args| {
|
|
2027
|
+
tishlang_runtime::promise_instance_then(&pc, args)
|
|
2028
|
+
}))
|
|
2029
|
+
}
|
|
2030
|
+
"catch" => {
|
|
2031
|
+
let pc = Arc::clone(p);
|
|
2032
|
+
Ok(Value::native(move |args| {
|
|
2033
|
+
tishlang_runtime::promise_instance_catch(&pc, args)
|
|
2034
|
+
}))
|
|
2035
|
+
}
|
|
2036
|
+
_ => Err(format!("Property '{}' not found", key)),
|
|
2037
|
+
},
|
|
2038
|
+
_ => Err(format!(
|
|
2039
|
+
"Cannot read property '{}' of {}",
|
|
2040
|
+
key,
|
|
2041
|
+
obj.type_name()
|
|
2042
|
+
)),
|
|
1599
2043
|
}
|
|
1600
2044
|
}
|
|
1601
2045
|
|