@tishlang/tish 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +1 -0
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/error.rs +2 -8
- package/crates/js_to_tish/src/transform/expr.rs +101 -130
- package/crates/js_to_tish/src/transform/stmt.rs +25 -22
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/cli_help.rs +76 -29
- package/crates/tish/src/main.rs +85 -54
- package/crates/tish/tests/cargo_example_compile.rs +67 -0
- package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
- package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
- package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
- package/crates/tish/tests/integration_test.rs +197 -47
- package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
- package/crates/tish/tests/shortcircuit.rs +19 -4
- package/crates/tish_ast/src/ast.rs +12 -14
- package/crates/tish_build_utils/src/lib.rs +64 -6
- package/crates/tish_builtins/src/array.rs +52 -21
- package/crates/tish_builtins/src/construct.rs +2 -8
- package/crates/tish_builtins/src/globals.rs +30 -15
- package/crates/tish_builtins/src/lib.rs +5 -5
- package/crates/tish_builtins/src/math.rs +5 -3
- package/crates/tish_builtins/src/string.rs +71 -19
- package/crates/tish_bytecode/src/chunk.rs +0 -1
- package/crates/tish_bytecode/src/compiler.rs +164 -60
- package/crates/tish_bytecode/src/opcode.rs +13 -4
- package/crates/tish_bytecode/src/peephole.rs +2 -2
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +989 -318
- package/crates/tish_compile/src/infer.rs +69 -19
- package/crates/tish_compile/src/lib.rs +21 -8
- package/crates/tish_compile/src/resolve.rs +515 -94
- package/crates/tish_compile/src/types.rs +10 -14
- package/crates/tish_compile_js/src/codegen.rs +34 -13
- 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 +40 -48
- package/crates/tish_core/src/json.rs +5 -3
- package/crates/tish_core/src/lib.rs +1 -1
- package/crates/tish_core/src/uri.rs +9 -6
- package/crates/tish_core/src/value.rs +92 -28
- package/crates/tish_cranelift/src/link.rs +6 -9
- package/crates/tish_cranelift/src/lower.rs +14 -8
- package/crates/tish_eval/src/eval.rs +398 -141
- package/crates/tish_eval/src/lib.rs +10 -6
- package/crates/tish_eval/src/natives.rs +95 -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 +10 -3
- package/crates/tish_fmt/src/lib.rs +29 -13
- package/crates/tish_lexer/src/lib.rs +217 -63
- package/crates/tish_lexer/src/token.rs +6 -6
- package/crates/tish_llvm/src/lib.rs +15 -8
- package/crates/tish_lsp/src/main.rs +41 -43
- package/crates/tish_native/src/build.rs +38 -15
- package/crates/tish_native/src/lib.rs +76 -32
- package/crates/tish_opt/src/lib.rs +67 -50
- package/crates/tish_parser/src/lib.rs +36 -11
- package/crates/tish_parser/src/parser.rs +172 -87
- package/crates/tish_runtime/src/http.rs +15 -6
- package/crates/tish_runtime/src/http_fetch.rs +24 -14
- package/crates/tish_runtime/src/lib.rs +224 -168
- package/crates/tish_runtime/src/promise.rs +1 -5
- package/crates/tish_runtime/src/ws.rs +45 -20
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
- package/crates/tish_ui/src/jsx.rs +41 -22
- package/crates/tish_ui/src/lib.rs +2 -2
- package/crates/tish_vm/src/vm.rs +320 -116
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +8 -3
- package/crates/tish_wasm/src/lib.rs +38 -28
- package/crates/tishlang_cargo_bindgen/Cargo.toml +25 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +52 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -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
|
@@ -8,9 +8,9 @@ use std::sync::Arc;
|
|
|
8
8
|
use tishlang_ast::{BinOp, UnaryOp};
|
|
9
9
|
use tishlang_builtins::array as arr_builtins;
|
|
10
10
|
use tishlang_builtins::construct as construct_builtin;
|
|
11
|
-
use tishlang_builtins::string as str_builtins;
|
|
12
11
|
use tishlang_builtins::globals as globals_builtins;
|
|
13
12
|
use tishlang_builtins::math as math_builtins;
|
|
13
|
+
use tishlang_builtins::string as str_builtins;
|
|
14
14
|
use tishlang_bytecode::{u8_to_binop, u8_to_unaryop, Chunk, Constant, Opcode, NO_REST_PARAM};
|
|
15
15
|
use tishlang_core::{ObjectMap, Value};
|
|
16
16
|
|
|
@@ -51,12 +51,24 @@ fn get_builtin_export(enabled: &HashSet<String>, spec: &str, export_name: &str)
|
|
|
51
51
|
#[cfg(feature = "fs")]
|
|
52
52
|
if spec == "tish:fs" && cap_allows(enabled, "fs") {
|
|
53
53
|
return match export_name {
|
|
54
|
-
"readFile" => Some(Value::Function(Rc::new(|args: &[Value]|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
"readFile" => Some(Value::Function(Rc::new(|args: &[Value]| {
|
|
55
|
+
tishlang_runtime::read_file(args)
|
|
56
|
+
}))),
|
|
57
|
+
"writeFile" => Some(Value::Function(Rc::new(|args: &[Value]| {
|
|
58
|
+
tishlang_runtime::write_file(args)
|
|
59
|
+
}))),
|
|
60
|
+
"fileExists" => Some(Value::Function(Rc::new(|args: &[Value]| {
|
|
61
|
+
tishlang_runtime::file_exists(args)
|
|
62
|
+
}))),
|
|
63
|
+
"isDir" => Some(Value::Function(Rc::new(|args: &[Value]| {
|
|
64
|
+
tishlang_runtime::is_dir(args)
|
|
65
|
+
}))),
|
|
66
|
+
"readDir" => Some(Value::Function(Rc::new(|args: &[Value]| {
|
|
67
|
+
tishlang_runtime::read_dir(args)
|
|
68
|
+
}))),
|
|
69
|
+
"mkdir" => Some(Value::Function(Rc::new(|args: &[Value]| {
|
|
70
|
+
tishlang_runtime::mkdir(args)
|
|
71
|
+
}))),
|
|
60
72
|
_ => None,
|
|
61
73
|
};
|
|
62
74
|
}
|
|
@@ -87,9 +99,15 @@ fn get_builtin_export(enabled: &HashSet<String>, spec: &str, export_name: &str)
|
|
|
87
99
|
#[cfg(feature = "process")]
|
|
88
100
|
if spec == "tish:process" && cap_allows(enabled, "process") {
|
|
89
101
|
return match export_name {
|
|
90
|
-
"exit" => Some(Value::Function(Rc::new(|args: &[Value]|
|
|
91
|
-
|
|
92
|
-
|
|
102
|
+
"exit" => Some(Value::Function(Rc::new(|args: &[Value]| {
|
|
103
|
+
tishlang_runtime::process_exit(args)
|
|
104
|
+
}))),
|
|
105
|
+
"cwd" => Some(Value::Function(Rc::new(|args: &[Value]| {
|
|
106
|
+
tishlang_runtime::process_cwd(args)
|
|
107
|
+
}))),
|
|
108
|
+
"exec" => Some(Value::Function(Rc::new(|args: &[Value]| {
|
|
109
|
+
tishlang_runtime::process_exec(args)
|
|
110
|
+
}))),
|
|
93
111
|
"argv" => Some(Value::Array(Rc::new(RefCell::new(
|
|
94
112
|
std::env::args().map(|s| Value::String(s.into())).collect(),
|
|
95
113
|
)))),
|
|
@@ -100,9 +118,24 @@ fn get_builtin_export(enabled: &HashSet<String>, spec: &str, export_name: &str)
|
|
|
100
118
|
)))),
|
|
101
119
|
"process" => {
|
|
102
120
|
let mut m = ObjectMap::default();
|
|
103
|
-
m.insert(
|
|
104
|
-
|
|
105
|
-
|
|
121
|
+
m.insert(
|
|
122
|
+
"exit".into(),
|
|
123
|
+
Value::Function(Rc::new(|args: &[Value]| {
|
|
124
|
+
tishlang_runtime::process_exit(args)
|
|
125
|
+
})),
|
|
126
|
+
);
|
|
127
|
+
m.insert(
|
|
128
|
+
"cwd".into(),
|
|
129
|
+
Value::Function(Rc::new(|args: &[Value]| {
|
|
130
|
+
tishlang_runtime::process_cwd(args)
|
|
131
|
+
})),
|
|
132
|
+
);
|
|
133
|
+
m.insert(
|
|
134
|
+
"exec".into(),
|
|
135
|
+
Value::Function(Rc::new(|args: &[Value]| {
|
|
136
|
+
tishlang_runtime::process_exec(args)
|
|
137
|
+
})),
|
|
138
|
+
);
|
|
106
139
|
m.insert(
|
|
107
140
|
"argv".into(),
|
|
108
141
|
Value::Array(Rc::new(RefCell::new(
|
|
@@ -134,7 +167,10 @@ fn get_builtin_export(enabled: &HashSet<String>, spec: &str, export_name: &str)
|
|
|
134
167
|
"wsSend" => Some(Value::Function(Rc::new(|args: &[Value]| {
|
|
135
168
|
Value::Bool(tishlang_runtime::ws_send_native(
|
|
136
169
|
args.first().unwrap_or(&Value::Null),
|
|
137
|
-
&args
|
|
170
|
+
&args
|
|
171
|
+
.get(1)
|
|
172
|
+
.map(|v| v.to_display_string())
|
|
173
|
+
.unwrap_or_default(),
|
|
138
174
|
))
|
|
139
175
|
}))),
|
|
140
176
|
"wsBroadcast" => Some(Value::Function(Rc::new(|args: &[Value]| {
|
|
@@ -183,7 +219,8 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
|
|
|
183
219
|
console.insert(
|
|
184
220
|
"debug".into(),
|
|
185
221
|
Value::Function(Rc::new(|args: &[Value]| {
|
|
186
|
-
let s =
|
|
222
|
+
let s =
|
|
223
|
+
tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
|
|
187
224
|
vm_log(&s);
|
|
188
225
|
Value::Null
|
|
189
226
|
})),
|
|
@@ -191,7 +228,8 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
|
|
|
191
228
|
console.insert(
|
|
192
229
|
"log".into(),
|
|
193
230
|
Value::Function(Rc::new(|args: &[Value]| {
|
|
194
|
-
let s =
|
|
231
|
+
let s =
|
|
232
|
+
tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
|
|
195
233
|
vm_log(&s);
|
|
196
234
|
Value::Null
|
|
197
235
|
})),
|
|
@@ -199,7 +237,8 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
|
|
|
199
237
|
console.insert(
|
|
200
238
|
"info".into(),
|
|
201
239
|
Value::Function(Rc::new(|args: &[Value]| {
|
|
202
|
-
let s =
|
|
240
|
+
let s =
|
|
241
|
+
tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
|
|
203
242
|
vm_log(&s);
|
|
204
243
|
Value::Null
|
|
205
244
|
})),
|
|
@@ -207,7 +246,8 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
|
|
|
207
246
|
console.insert(
|
|
208
247
|
"warn".into(),
|
|
209
248
|
Value::Function(Rc::new(|args: &[Value]| {
|
|
210
|
-
let s =
|
|
249
|
+
let s =
|
|
250
|
+
tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
|
|
211
251
|
vm_log_err(&s);
|
|
212
252
|
Value::Null
|
|
213
253
|
})),
|
|
@@ -215,12 +255,16 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
|
|
|
215
255
|
console.insert(
|
|
216
256
|
"error".into(),
|
|
217
257
|
Value::Function(Rc::new(|args: &[Value]| {
|
|
218
|
-
let s =
|
|
258
|
+
let s =
|
|
259
|
+
tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
|
|
219
260
|
vm_log_err(&s);
|
|
220
261
|
Value::Null
|
|
221
262
|
})),
|
|
222
263
|
);
|
|
223
|
-
g.insert(
|
|
264
|
+
g.insert(
|
|
265
|
+
"console".into(),
|
|
266
|
+
Value::Object(Rc::new(RefCell::new(console))),
|
|
267
|
+
);
|
|
224
268
|
|
|
225
269
|
let mut math = ObjectMap::default();
|
|
226
270
|
math.insert(
|
|
@@ -276,14 +320,38 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
|
|
|
276
320
|
Value::Number(nums.into_iter().fold(f64::NAN, |a, b| a.max(b)))
|
|
277
321
|
})),
|
|
278
322
|
);
|
|
279
|
-
math.insert(
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
math.insert(
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
323
|
+
math.insert(
|
|
324
|
+
"pow".into(),
|
|
325
|
+
Value::Function(Rc::new(|args: &[Value]| math_builtins::pow(args))),
|
|
326
|
+
);
|
|
327
|
+
math.insert(
|
|
328
|
+
"sin".into(),
|
|
329
|
+
Value::Function(Rc::new(|args: &[Value]| math_builtins::sin(args))),
|
|
330
|
+
);
|
|
331
|
+
math.insert(
|
|
332
|
+
"cos".into(),
|
|
333
|
+
Value::Function(Rc::new(|args: &[Value]| math_builtins::cos(args))),
|
|
334
|
+
);
|
|
335
|
+
math.insert(
|
|
336
|
+
"tan".into(),
|
|
337
|
+
Value::Function(Rc::new(|args: &[Value]| math_builtins::tan(args))),
|
|
338
|
+
);
|
|
339
|
+
math.insert(
|
|
340
|
+
"log".into(),
|
|
341
|
+
Value::Function(Rc::new(|args: &[Value]| math_builtins::log(args))),
|
|
342
|
+
);
|
|
343
|
+
math.insert(
|
|
344
|
+
"exp".into(),
|
|
345
|
+
Value::Function(Rc::new(|args: &[Value]| math_builtins::exp(args))),
|
|
346
|
+
);
|
|
347
|
+
math.insert(
|
|
348
|
+
"sign".into(),
|
|
349
|
+
Value::Function(Rc::new(|args: &[Value]| math_builtins::sign(args))),
|
|
350
|
+
);
|
|
351
|
+
math.insert(
|
|
352
|
+
"trunc".into(),
|
|
353
|
+
Value::Function(Rc::new(|args: &[Value]| math_builtins::trunc(args))),
|
|
354
|
+
);
|
|
287
355
|
math.insert("PI".into(), Value::Number(std::f64::consts::PI));
|
|
288
356
|
math.insert("E".into(), Value::Number(std::f64::consts::E));
|
|
289
357
|
g.insert("Math".into(), Value::Object(Rc::new(RefCell::new(math))));
|
|
@@ -292,7 +360,10 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
|
|
|
292
360
|
json.insert(
|
|
293
361
|
"parse".into(),
|
|
294
362
|
Value::Function(Rc::new(|args: &[Value]| {
|
|
295
|
-
let s = args
|
|
363
|
+
let s = args
|
|
364
|
+
.first()
|
|
365
|
+
.map(|v| v.to_display_string())
|
|
366
|
+
.unwrap_or_default();
|
|
296
367
|
tishlang_core::json_parse(&s).unwrap_or(Value::Null)
|
|
297
368
|
})),
|
|
298
369
|
);
|
|
@@ -305,13 +376,36 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
|
|
|
305
376
|
);
|
|
306
377
|
g.insert("JSON".into(), Value::Object(Rc::new(RefCell::new(json))));
|
|
307
378
|
|
|
308
|
-
g.insert(
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
g.insert(
|
|
313
|
-
|
|
314
|
-
|
|
379
|
+
g.insert(
|
|
380
|
+
"parseInt".into(),
|
|
381
|
+
Value::Function(Rc::new(|args: &[Value]| globals_builtins::parse_int(args))),
|
|
382
|
+
);
|
|
383
|
+
g.insert(
|
|
384
|
+
"parseFloat".into(),
|
|
385
|
+
Value::Function(Rc::new(|args: &[Value]| {
|
|
386
|
+
globals_builtins::parse_float(args)
|
|
387
|
+
})),
|
|
388
|
+
);
|
|
389
|
+
g.insert(
|
|
390
|
+
"encodeURI".into(),
|
|
391
|
+
Value::Function(Rc::new(|args: &[Value]| globals_builtins::encode_uri(args))),
|
|
392
|
+
);
|
|
393
|
+
g.insert(
|
|
394
|
+
"decodeURI".into(),
|
|
395
|
+
Value::Function(Rc::new(|args: &[Value]| globals_builtins::decode_uri(args))),
|
|
396
|
+
);
|
|
397
|
+
g.insert(
|
|
398
|
+
"Boolean".into(),
|
|
399
|
+
Value::Function(Rc::new(|args: &[Value]| globals_builtins::boolean(args))),
|
|
400
|
+
);
|
|
401
|
+
g.insert(
|
|
402
|
+
"isFinite".into(),
|
|
403
|
+
Value::Function(Rc::new(|args: &[Value]| globals_builtins::is_finite(args))),
|
|
404
|
+
);
|
|
405
|
+
g.insert(
|
|
406
|
+
"isNaN".into(),
|
|
407
|
+
Value::Function(Rc::new(|args: &[Value]| globals_builtins::is_nan(args))),
|
|
408
|
+
);
|
|
315
409
|
g.insert("Infinity".into(), Value::Number(f64::INFINITY));
|
|
316
410
|
g.insert("NaN".into(), Value::Number(f64::NAN));
|
|
317
411
|
g.insert(
|
|
@@ -349,31 +443,68 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
|
|
|
349
443
|
let mut object_methods = ObjectMap::default();
|
|
350
444
|
object_methods.insert(
|
|
351
445
|
"assign".into(),
|
|
352
|
-
Value::Function(Rc::new(|args: &[Value]|
|
|
446
|
+
Value::Function(Rc::new(|args: &[Value]| {
|
|
447
|
+
globals_builtins::object_assign(args)
|
|
448
|
+
})),
|
|
353
449
|
);
|
|
354
450
|
object_methods.insert(
|
|
355
451
|
"fromEntries".into(),
|
|
356
|
-
Value::Function(Rc::new(|args: &[Value]|
|
|
452
|
+
Value::Function(Rc::new(|args: &[Value]| {
|
|
453
|
+
globals_builtins::object_from_entries(args)
|
|
454
|
+
})),
|
|
455
|
+
);
|
|
456
|
+
object_methods.insert(
|
|
457
|
+
"keys".into(),
|
|
458
|
+
Value::Function(Rc::new(|args: &[Value]| {
|
|
459
|
+
globals_builtins::object_keys(args)
|
|
460
|
+
})),
|
|
461
|
+
);
|
|
462
|
+
object_methods.insert(
|
|
463
|
+
"values".into(),
|
|
464
|
+
Value::Function(Rc::new(|args: &[Value]| {
|
|
465
|
+
globals_builtins::object_values(args)
|
|
466
|
+
})),
|
|
467
|
+
);
|
|
468
|
+
object_methods.insert(
|
|
469
|
+
"entries".into(),
|
|
470
|
+
Value::Function(Rc::new(|args: &[Value]| {
|
|
471
|
+
globals_builtins::object_entries(args)
|
|
472
|
+
})),
|
|
473
|
+
);
|
|
474
|
+
g.insert(
|
|
475
|
+
"Object".into(),
|
|
476
|
+
Value::Object(Rc::new(RefCell::new(object_methods))),
|
|
357
477
|
);
|
|
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
478
|
|
|
363
479
|
// Array.isArray
|
|
364
480
|
let mut array_static = ObjectMap::default();
|
|
365
481
|
array_static.insert(
|
|
366
482
|
"isArray".into(),
|
|
367
|
-
Value::Function(Rc::new(|args: &[Value]|
|
|
483
|
+
Value::Function(Rc::new(|args: &[Value]| {
|
|
484
|
+
globals_builtins::array_is_array(args)
|
|
485
|
+
})),
|
|
486
|
+
);
|
|
487
|
+
g.insert(
|
|
488
|
+
"Array".into(),
|
|
489
|
+
Value::Object(Rc::new(RefCell::new(array_static))),
|
|
368
490
|
);
|
|
369
|
-
g.insert("Array".into(), Value::Object(Rc::new(RefCell::new(array_static))));
|
|
370
491
|
|
|
371
492
|
// String(value) as callable + String.fromCharCode
|
|
372
|
-
let string_convert_fn = Value::Function(Rc::new(|args: &[Value]|
|
|
493
|
+
let string_convert_fn = Value::Function(Rc::new(|args: &[Value]| {
|
|
494
|
+
globals_builtins::string_convert(args)
|
|
495
|
+
}));
|
|
373
496
|
let mut string_static = ObjectMap::default();
|
|
374
|
-
string_static.insert(
|
|
497
|
+
string_static.insert(
|
|
498
|
+
"fromCharCode".into(),
|
|
499
|
+
Value::Function(Rc::new(|args: &[Value]| {
|
|
500
|
+
globals_builtins::string_from_char_code(args)
|
|
501
|
+
})),
|
|
502
|
+
);
|
|
375
503
|
string_static.insert(Arc::from("__call"), string_convert_fn);
|
|
376
|
-
g.insert(
|
|
504
|
+
g.insert(
|
|
505
|
+
"String".into(),
|
|
506
|
+
Value::Object(Rc::new(RefCell::new(string_static))),
|
|
507
|
+
);
|
|
377
508
|
|
|
378
509
|
// JSX / Lattish: stubs for bytecode VM when no DOM (e.g. console). Override via set_global in browser.
|
|
379
510
|
g.insert(
|
|
@@ -405,22 +536,31 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
|
|
|
405
536
|
);
|
|
406
537
|
let mut document_obj = ObjectMap::default();
|
|
407
538
|
document_obj.insert("body".into(), Value::Null);
|
|
408
|
-
g.insert(
|
|
539
|
+
g.insert(
|
|
540
|
+
"document".into(),
|
|
541
|
+
Value::Object(Rc::new(RefCell::new(document_obj))),
|
|
542
|
+
);
|
|
409
543
|
|
|
410
544
|
#[cfg(feature = "process")]
|
|
411
545
|
if cap_allows(enabled, "process") {
|
|
412
546
|
let mut process_obj = ObjectMap::default();
|
|
413
547
|
process_obj.insert(
|
|
414
548
|
"exit".into(),
|
|
415
|
-
Value::Function(Rc::new(|args: &[Value]|
|
|
549
|
+
Value::Function(Rc::new(|args: &[Value]| {
|
|
550
|
+
tishlang_runtime::process_exit(args)
|
|
551
|
+
})),
|
|
416
552
|
);
|
|
417
553
|
process_obj.insert(
|
|
418
554
|
"cwd".into(),
|
|
419
|
-
Value::Function(Rc::new(|args: &[Value]|
|
|
555
|
+
Value::Function(Rc::new(|args: &[Value]| {
|
|
556
|
+
tishlang_runtime::process_cwd(args)
|
|
557
|
+
})),
|
|
420
558
|
);
|
|
421
559
|
process_obj.insert(
|
|
422
560
|
"exec".into(),
|
|
423
|
-
Value::Function(Rc::new(|args: &[Value]|
|
|
561
|
+
Value::Function(Rc::new(|args: &[Value]| {
|
|
562
|
+
tishlang_runtime::process_exec(args)
|
|
563
|
+
})),
|
|
424
564
|
);
|
|
425
565
|
process_obj.insert(
|
|
426
566
|
"argv".into(),
|
|
@@ -436,7 +576,10 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
|
|
|
436
576
|
.collect(),
|
|
437
577
|
))),
|
|
438
578
|
);
|
|
439
|
-
g.insert(
|
|
579
|
+
g.insert(
|
|
580
|
+
"process".into(),
|
|
581
|
+
Value::Object(Rc::new(RefCell::new(process_obj))),
|
|
582
|
+
);
|
|
440
583
|
}
|
|
441
584
|
|
|
442
585
|
#[cfg(feature = "http")]
|
|
@@ -511,7 +654,11 @@ impl Vm {
|
|
|
511
654
|
|
|
512
655
|
/// Names of all globals (for REPL bare-word tab completion).
|
|
513
656
|
pub fn global_names(&self) -> Vec<String> {
|
|
514
|
-
self.globals
|
|
657
|
+
self.globals
|
|
658
|
+
.borrow()
|
|
659
|
+
.keys()
|
|
660
|
+
.map(|k| k.as_ref().to_string())
|
|
661
|
+
.collect()
|
|
515
662
|
}
|
|
516
663
|
|
|
517
664
|
fn read_u16(code: &[u8], ip: &mut usize) -> u16 {
|
|
@@ -663,9 +810,7 @@ impl Vm {
|
|
|
663
810
|
} else if self.scope.contains_key(name.as_ref()) {
|
|
664
811
|
self.scope.insert(Arc::clone(name), v);
|
|
665
812
|
} else if self.globals.borrow().contains_key(name.as_ref()) {
|
|
666
|
-
self.globals
|
|
667
|
-
.borrow_mut()
|
|
668
|
-
.insert(Arc::clone(name), v);
|
|
813
|
+
self.globals.borrow_mut().insert(Arc::clone(name), v);
|
|
669
814
|
} else {
|
|
670
815
|
// New variable: at top level (no enclosing) store in globals so REPL persists across lines
|
|
671
816
|
if self.enclosing.is_none() {
|
|
@@ -716,9 +861,9 @@ impl Vm {
|
|
|
716
861
|
block_undo_stack.push(Vec::new());
|
|
717
862
|
}
|
|
718
863
|
Opcode::ExitBlock => {
|
|
719
|
-
let frame = block_undo_stack
|
|
720
|
-
|
|
721
|
-
|
|
864
|
+
let frame = block_undo_stack
|
|
865
|
+
.pop()
|
|
866
|
+
.ok_or_else(|| "ExitBlock without matching EnterBlock".to_string())?;
|
|
722
867
|
for (name, old) in frame.into_iter().rev() {
|
|
723
868
|
let mut ls = local_scope.borrow_mut();
|
|
724
869
|
match old {
|
|
@@ -753,17 +898,19 @@ impl Vm {
|
|
|
753
898
|
.stack
|
|
754
899
|
.pop()
|
|
755
900
|
.ok_or_else(|| "Stack underflow".to_string())?;
|
|
756
|
-
self.globals
|
|
757
|
-
.borrow_mut()
|
|
758
|
-
.insert(Arc::clone(name), v);
|
|
901
|
+
self.globals.borrow_mut().insert(Arc::clone(name), v);
|
|
759
902
|
}
|
|
760
903
|
Opcode::Pop => {
|
|
761
|
-
self.stack
|
|
904
|
+
self.stack
|
|
905
|
+
.pop()
|
|
906
|
+
.ok_or_else(|| "Stack underflow".to_string())?;
|
|
762
907
|
}
|
|
763
908
|
Opcode::PopN => {
|
|
764
909
|
let n = Self::read_u16(code, &mut ip) as usize;
|
|
765
910
|
for _ in 0..n {
|
|
766
|
-
self.stack
|
|
911
|
+
self.stack
|
|
912
|
+
.pop()
|
|
913
|
+
.ok_or_else(|| "Stack underflow".to_string())?;
|
|
767
914
|
}
|
|
768
915
|
}
|
|
769
916
|
Opcode::Dup => {
|
|
@@ -792,7 +939,9 @@ impl Vm {
|
|
|
792
939
|
let f = match &callee {
|
|
793
940
|
Value::Function(f) => Rc::clone(f),
|
|
794
941
|
Value::Object(o) => {
|
|
795
|
-
if let Some(Value::Function(call_fn)) =
|
|
942
|
+
if let Some(Value::Function(call_fn)) =
|
|
943
|
+
o.borrow().get(&Arc::from("__call"))
|
|
944
|
+
{
|
|
796
945
|
Rc::clone(call_fn)
|
|
797
946
|
} else {
|
|
798
947
|
return Err(format!(
|
|
@@ -802,10 +951,7 @@ impl Vm {
|
|
|
802
951
|
}
|
|
803
952
|
}
|
|
804
953
|
_ => {
|
|
805
|
-
return Err(format!(
|
|
806
|
-
"Call of non-function: {}",
|
|
807
|
-
callee.type_name()
|
|
808
|
-
));
|
|
954
|
+
return Err(format!("Call of non-function: {}", callee.type_name()));
|
|
809
955
|
}
|
|
810
956
|
};
|
|
811
957
|
let result = f(&args);
|
|
@@ -832,7 +978,9 @@ impl Vm {
|
|
|
832
978
|
let f = match &callee {
|
|
833
979
|
Value::Function(f) => Rc::clone(f),
|
|
834
980
|
Value::Object(o) => {
|
|
835
|
-
if let Some(Value::Function(call_fn)) =
|
|
981
|
+
if let Some(Value::Function(call_fn)) =
|
|
982
|
+
o.borrow().get(&Arc::from("__call"))
|
|
983
|
+
{
|
|
836
984
|
Rc::clone(call_fn)
|
|
837
985
|
} else {
|
|
838
986
|
return Err(format!(
|
|
@@ -842,10 +990,7 @@ impl Vm {
|
|
|
842
990
|
}
|
|
843
991
|
}
|
|
844
992
|
_ => {
|
|
845
|
-
return Err(format!(
|
|
846
|
-
"Call of non-function: {}",
|
|
847
|
-
callee.type_name()
|
|
848
|
-
));
|
|
993
|
+
return Err(format!("Call of non-function: {}", callee.type_name()));
|
|
849
994
|
}
|
|
850
995
|
};
|
|
851
996
|
let result = f(&args);
|
|
@@ -922,8 +1067,8 @@ impl Vm {
|
|
|
922
1067
|
.stack
|
|
923
1068
|
.pop()
|
|
924
1069
|
.ok_or_else(|| "Stack underflow".to_string())?;
|
|
925
|
-
let op =
|
|
926
|
-
.ok_or_else(|| format!("Unknown binop: {}", op_u8))?;
|
|
1070
|
+
let op =
|
|
1071
|
+
u8_to_binop(op_u8).ok_or_else(|| format!("Unknown binop: {}", op_u8))?;
|
|
927
1072
|
let result = eval_binop(op, &l, &r)?;
|
|
928
1073
|
self.stack.push(result);
|
|
929
1074
|
}
|
|
@@ -1023,8 +1168,7 @@ impl Vm {
|
|
|
1023
1168
|
);
|
|
1024
1169
|
}
|
|
1025
1170
|
elems.reverse();
|
|
1026
|
-
self.stack
|
|
1027
|
-
.push(Value::Array(Rc::new(RefCell::new(elems))));
|
|
1171
|
+
self.stack.push(Value::Array(Rc::new(RefCell::new(elems))));
|
|
1028
1172
|
}
|
|
1029
1173
|
Opcode::NewObject => {
|
|
1030
1174
|
let n = Self::read_u16(code, &mut ip) as usize;
|
|
@@ -1041,8 +1185,7 @@ impl Vm {
|
|
|
1041
1185
|
let key = key_val.to_display_string().into();
|
|
1042
1186
|
map.insert(key, val);
|
|
1043
1187
|
}
|
|
1044
|
-
self.stack
|
|
1045
|
-
.push(Value::Object(Rc::new(RefCell::new(map))));
|
|
1188
|
+
self.stack.push(Value::Object(Rc::new(RefCell::new(map))));
|
|
1046
1189
|
}
|
|
1047
1190
|
Opcode::EnterTry => {
|
|
1048
1191
|
let offset = Self::read_u16(code, &mut ip) as usize;
|
|
@@ -1159,9 +1302,7 @@ impl Vm {
|
|
|
1159
1302
|
.pop()
|
|
1160
1303
|
.ok_or_else(|| "Stack underflow".to_string())?;
|
|
1161
1304
|
let result = match &arr {
|
|
1162
|
-
Value::Array(a) =>
|
|
1163
|
-
Value::Array(Rc::new(RefCell::new(a.borrow().clone())))
|
|
1164
|
-
}
|
|
1305
|
+
Value::Array(a) => Value::Array(Rc::new(RefCell::new(a.borrow().clone()))),
|
|
1165
1306
|
_ => Value::Null,
|
|
1166
1307
|
};
|
|
1167
1308
|
self.stack.push(result);
|
|
@@ -1187,8 +1328,16 @@ impl Vm {
|
|
|
1187
1328
|
let mapped: Vec<Value> = arr_borrow
|
|
1188
1329
|
.iter()
|
|
1189
1330
|
.map(|v| {
|
|
1190
|
-
let l: Value = if param_left {
|
|
1191
|
-
|
|
1331
|
+
let l: Value = if param_left {
|
|
1332
|
+
(*v).clone()
|
|
1333
|
+
} else {
|
|
1334
|
+
const_val.clone()
|
|
1335
|
+
};
|
|
1336
|
+
let r: Value = if param_left {
|
|
1337
|
+
const_val.clone()
|
|
1338
|
+
} else {
|
|
1339
|
+
(*v).clone()
|
|
1340
|
+
};
|
|
1192
1341
|
eval_binop(binop, &l, &r).unwrap_or(Value::Null)
|
|
1193
1342
|
})
|
|
1194
1343
|
.collect();
|
|
@@ -1204,8 +1353,9 @@ impl Vm {
|
|
|
1204
1353
|
let const_idx = Self::read_u16(code, &mut ip);
|
|
1205
1354
|
let param_left = code[ip] == 0; // 0 = param on left (x op const), 1 = param on right (const op x)
|
|
1206
1355
|
ip += 1;
|
|
1207
|
-
let binop = u8_to_binop(binop_u8)
|
|
1208
|
-
|
|
1356
|
+
let binop = u8_to_binop(binop_u8).ok_or_else(|| {
|
|
1357
|
+
format!("Unknown binop in ArrayFilterBinOp: {}", binop_u8)
|
|
1358
|
+
})?;
|
|
1209
1359
|
let arr = self
|
|
1210
1360
|
.stack
|
|
1211
1361
|
.pop()
|
|
@@ -1219,8 +1369,16 @@ impl Vm {
|
|
|
1219
1369
|
let filtered: Vec<Value> = arr_borrow
|
|
1220
1370
|
.iter()
|
|
1221
1371
|
.filter(|v| {
|
|
1222
|
-
let l: Value = if param_left {
|
|
1223
|
-
|
|
1372
|
+
let l: Value = if param_left {
|
|
1373
|
+
(*v).clone()
|
|
1374
|
+
} else {
|
|
1375
|
+
const_val.clone()
|
|
1376
|
+
};
|
|
1377
|
+
let r: Value = if param_left {
|
|
1378
|
+
const_val.clone()
|
|
1379
|
+
} else {
|
|
1380
|
+
(*v).clone()
|
|
1381
|
+
};
|
|
1224
1382
|
let b = eval_binop(binop, &l, &r).unwrap_or(Value::Null);
|
|
1225
1383
|
b.is_truthy()
|
|
1226
1384
|
})
|
|
@@ -1250,7 +1408,10 @@ impl Vm {
|
|
|
1250
1408
|
let spec = match constants.get(spec_idx as usize) {
|
|
1251
1409
|
Some(Constant::String(s)) => s.as_ref(),
|
|
1252
1410
|
_ => {
|
|
1253
|
-
return Err(
|
|
1411
|
+
return Err(
|
|
1412
|
+
"LoadNativeExport: spec constant out of bounds or not string"
|
|
1413
|
+
.to_string(),
|
|
1414
|
+
);
|
|
1254
1415
|
}
|
|
1255
1416
|
};
|
|
1256
1417
|
let export_name = match constants.get(export_idx as usize) {
|
|
@@ -1260,10 +1421,17 @@ impl Vm {
|
|
|
1260
1421
|
}
|
|
1261
1422
|
};
|
|
1262
1423
|
let v = get_builtin_export(self.capabilities.as_ref(), spec, export_name).ok_or_else(|| {
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1424
|
+
if spec.starts_with("cargo:") {
|
|
1425
|
+
format!(
|
|
1426
|
+
"cargo:… imports are only supported by `tish build` with the Rust native backend (not the bytecode VM). Spec: {}",
|
|
1427
|
+
spec
|
|
1428
|
+
)
|
|
1429
|
+
} else {
|
|
1430
|
+
format!(
|
|
1431
|
+
"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.",
|
|
1432
|
+
spec, export_name
|
|
1433
|
+
)
|
|
1434
|
+
}
|
|
1267
1435
|
})?;
|
|
1268
1436
|
self.stack.push(v);
|
|
1269
1437
|
}
|
|
@@ -1393,33 +1561,50 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
|
|
|
1393
1561
|
match obj {
|
|
1394
1562
|
Value::Object(m) => {
|
|
1395
1563
|
let map = m.borrow();
|
|
1396
|
-
map.get(key.as_ref())
|
|
1397
|
-
|
|
1398
|
-
|
|
1564
|
+
map.get(key.as_ref())
|
|
1565
|
+
.cloned()
|
|
1566
|
+
.ok_or_else(|| format!("Property '{}' not found", key))
|
|
1399
1567
|
}
|
|
1400
1568
|
Value::Array(a) => {
|
|
1401
1569
|
let key_s = key.as_ref();
|
|
1402
1570
|
if let Ok(idx) = key_s.parse::<usize>() {
|
|
1403
1571
|
let arr = a.borrow();
|
|
1404
|
-
return arr
|
|
1572
|
+
return arr
|
|
1573
|
+
.get(idx)
|
|
1574
|
+
.cloned()
|
|
1575
|
+
.ok_or_else(|| "Index out of bounds".to_string());
|
|
1405
1576
|
}
|
|
1406
1577
|
if key_s == "length" {
|
|
1407
1578
|
return Ok(Value::Number(a.borrow().len() as f64));
|
|
1408
1579
|
}
|
|
1409
1580
|
let a_clone = Rc::clone(a);
|
|
1410
1581
|
let method: ArrayMethodFn = match key_s {
|
|
1411
|
-
"push" => Rc::new(move |args: &[Value]|
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
"
|
|
1415
|
-
|
|
1416
|
-
|
|
1582
|
+
"push" => Rc::new(move |args: &[Value]| {
|
|
1583
|
+
arr_builtins::push(&Value::Array(Rc::clone(&a_clone)), args)
|
|
1584
|
+
}),
|
|
1585
|
+
"pop" => Rc::new(move |_args: &[Value]| {
|
|
1586
|
+
arr_builtins::pop(&Value::Array(Rc::clone(&a_clone)))
|
|
1587
|
+
}),
|
|
1588
|
+
"shift" => Rc::new(move |_args: &[Value]| {
|
|
1589
|
+
arr_builtins::shift(&Value::Array(Rc::clone(&a_clone)))
|
|
1590
|
+
}),
|
|
1591
|
+
"unshift" => Rc::new(move |args: &[Value]| {
|
|
1592
|
+
arr_builtins::unshift(&Value::Array(Rc::clone(&a_clone)), args)
|
|
1593
|
+
}),
|
|
1594
|
+
"reverse" => Rc::new(move |_args: &[Value]| {
|
|
1595
|
+
arr_builtins::reverse(&Value::Array(Rc::clone(&a_clone)))
|
|
1596
|
+
}),
|
|
1597
|
+
"shuffle" => Rc::new(move |_args: &[Value]| {
|
|
1598
|
+
arr_builtins::shuffle(&Value::Array(Rc::clone(&a_clone)))
|
|
1599
|
+
}),
|
|
1417
1600
|
"slice" => Rc::new(move |args: &[Value]| {
|
|
1418
1601
|
let start = args.first().unwrap_or(&Value::Null);
|
|
1419
1602
|
let end = args.get(1).unwrap_or(&Value::Null);
|
|
1420
1603
|
arr_builtins::slice(&Value::Array(Rc::clone(&a_clone)), start, end)
|
|
1421
1604
|
}),
|
|
1422
|
-
"concat" => Rc::new(move |args: &[Value]|
|
|
1605
|
+
"concat" => Rc::new(move |args: &[Value]| {
|
|
1606
|
+
arr_builtins::concat(&Value::Array(Rc::clone(&a_clone)), args)
|
|
1607
|
+
}),
|
|
1423
1608
|
"join" => Rc::new(move |args: &[Value]| {
|
|
1424
1609
|
let sep = args.first().unwrap_or(&Value::Null);
|
|
1425
1610
|
arr_builtins::join(&Value::Array(Rc::clone(&a_clone)), sep)
|
|
@@ -1477,7 +1662,10 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
|
|
|
1477
1662
|
"sort" => Rc::new(move |args: &[Value]| {
|
|
1478
1663
|
let cmp = args.first();
|
|
1479
1664
|
if let Some(Value::Function(_)) = cmp {
|
|
1480
|
-
arr_builtins::sort_with_comparator(
|
|
1665
|
+
arr_builtins::sort_with_comparator(
|
|
1666
|
+
&Value::Array(Rc::clone(&a_clone)),
|
|
1667
|
+
cmp.unwrap(),
|
|
1668
|
+
)
|
|
1481
1669
|
} else {
|
|
1482
1670
|
arr_builtins::sort_default(&Value::Array(Rc::clone(&a_clone)))
|
|
1483
1671
|
}
|
|
@@ -1486,7 +1674,12 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
|
|
|
1486
1674
|
let start = args.first().unwrap_or(&Value::Null);
|
|
1487
1675
|
let delete_count = args.get(1).map(|v| v as &Value);
|
|
1488
1676
|
let items: Vec<Value> = args.get(2..).unwrap_or(&[]).to_vec();
|
|
1489
|
-
arr_builtins::splice(
|
|
1677
|
+
arr_builtins::splice(
|
|
1678
|
+
&Value::Array(Rc::clone(&a_clone)),
|
|
1679
|
+
start,
|
|
1680
|
+
delete_count,
|
|
1681
|
+
&items,
|
|
1682
|
+
)
|
|
1490
1683
|
}),
|
|
1491
1684
|
_ => return Err(format!("Property '{}' not found", key)),
|
|
1492
1685
|
};
|
|
@@ -1512,10 +1705,7 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
|
|
|
1512
1705
|
}),
|
|
1513
1706
|
"lastIndexOf" => Rc::new(move |args: &[Value]| {
|
|
1514
1707
|
let search = args.first().unwrap_or(&Value::Null);
|
|
1515
|
-
let position = args
|
|
1516
|
-
.get(1)
|
|
1517
|
-
.cloned()
|
|
1518
|
-
.unwrap_or(Value::Number(f64::INFINITY));
|
|
1708
|
+
let position = args.get(1).cloned().unwrap_or(Value::Number(f64::INFINITY));
|
|
1519
1709
|
str_builtins::last_index_of(
|
|
1520
1710
|
&Value::String(Arc::clone(&s_clone)),
|
|
1521
1711
|
search,
|
|
@@ -1541,9 +1731,15 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
|
|
|
1541
1731
|
let sep = args.first().unwrap_or(&Value::Null);
|
|
1542
1732
|
str_builtins::split(&Value::String(Arc::clone(&s_clone)), sep)
|
|
1543
1733
|
}),
|
|
1544
|
-
"trim" => Rc::new(move |_args: &[Value]|
|
|
1545
|
-
|
|
1546
|
-
|
|
1734
|
+
"trim" => Rc::new(move |_args: &[Value]| {
|
|
1735
|
+
str_builtins::trim(&Value::String(Arc::clone(&s_clone)))
|
|
1736
|
+
}),
|
|
1737
|
+
"toUpperCase" => Rc::new(move |_args: &[Value]| {
|
|
1738
|
+
str_builtins::to_upper_case(&Value::String(Arc::clone(&s_clone)))
|
|
1739
|
+
}),
|
|
1740
|
+
"toLowerCase" => Rc::new(move |_args: &[Value]| {
|
|
1741
|
+
str_builtins::to_lower_case(&Value::String(Arc::clone(&s_clone)))
|
|
1742
|
+
}),
|
|
1547
1743
|
"startsWith" => Rc::new(move |args: &[Value]| {
|
|
1548
1744
|
let search = args.first().unwrap_or(&Value::Null);
|
|
1549
1745
|
str_builtins::starts_with(&Value::String(Arc::clone(&s_clone)), search)
|
|
@@ -1560,7 +1756,11 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
|
|
|
1560
1756
|
"replaceAll" => Rc::new(move |args: &[Value]| {
|
|
1561
1757
|
let search = args.first().unwrap_or(&Value::Null);
|
|
1562
1758
|
let replacement = args.get(1).unwrap_or(&Value::Null);
|
|
1563
|
-
str_builtins::replace_all(
|
|
1759
|
+
str_builtins::replace_all(
|
|
1760
|
+
&Value::String(Arc::clone(&s_clone)),
|
|
1761
|
+
search,
|
|
1762
|
+
replacement,
|
|
1763
|
+
)
|
|
1564
1764
|
}),
|
|
1565
1765
|
"charAt" => Rc::new(move |args: &[Value]| {
|
|
1566
1766
|
let idx = args.first().unwrap_or(&Value::Null);
|
|
@@ -1588,7 +1788,11 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
|
|
|
1588
1788
|
};
|
|
1589
1789
|
Ok(Value::Function(method))
|
|
1590
1790
|
}
|
|
1591
|
-
_ => Err(format!(
|
|
1791
|
+
_ => Err(format!(
|
|
1792
|
+
"Cannot read property '{}' of {}",
|
|
1793
|
+
key,
|
|
1794
|
+
obj.type_name()
|
|
1795
|
+
)),
|
|
1592
1796
|
}
|
|
1593
1797
|
}
|
|
1594
1798
|
|