@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
|
@@ -4,9 +4,9 @@ use std::collections::HashMap;
|
|
|
4
4
|
use std::sync::Arc;
|
|
5
5
|
|
|
6
6
|
use tishlang_ast::{
|
|
7
|
-
ArrayElement, ArrowBody, BinOp, CallArg, DestructElement, DestructPattern,
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
ArrayElement, ArrowBody, BinOp, CallArg, DestructElement, DestructPattern, ExportDeclaration,
|
|
8
|
+
Expr, FunParam, JsxAttrValue, JsxChild, JsxProp, Literal, LogicalAssignOp, MemberProp,
|
|
9
|
+
ObjectProp, Program, Span, Statement,
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
use crate::chunk::{Chunk, Constant};
|
|
@@ -120,9 +120,7 @@ impl<'a> Compiler<'a> {
|
|
|
120
120
|
slots.push(None);
|
|
121
121
|
}
|
|
122
122
|
FunParam::Destructure {
|
|
123
|
-
pattern,
|
|
124
|
-
default,
|
|
125
|
-
..
|
|
123
|
+
pattern, default, ..
|
|
126
124
|
} => {
|
|
127
125
|
if default.is_some() {
|
|
128
126
|
return Err(CompileError {
|
|
@@ -274,8 +272,18 @@ impl<'a> Compiler<'a> {
|
|
|
274
272
|
..
|
|
275
273
|
} = body_expr
|
|
276
274
|
{
|
|
277
|
-
if let (
|
|
278
|
-
|
|
275
|
+
if let (
|
|
276
|
+
Expr::Member {
|
|
277
|
+
object: lo,
|
|
278
|
+
prop: MemberProp::Name { name: p, .. },
|
|
279
|
+
..
|
|
280
|
+
},
|
|
281
|
+
Expr::Member {
|
|
282
|
+
object: ro,
|
|
283
|
+
prop: MemberProp::Name { name: pr, .. },
|
|
284
|
+
..
|
|
285
|
+
},
|
|
286
|
+
) = (left.as_ref(), right.as_ref())
|
|
279
287
|
{
|
|
280
288
|
if p != pr {
|
|
281
289
|
return None;
|
|
@@ -327,8 +335,14 @@ impl<'a> Compiler<'a> {
|
|
|
327
335
|
..
|
|
328
336
|
} = body_expr
|
|
329
337
|
{
|
|
330
|
-
if let (
|
|
331
|
-
|
|
338
|
+
if let (
|
|
339
|
+
Expr::Ident {
|
|
340
|
+
name: left_name, ..
|
|
341
|
+
},
|
|
342
|
+
Expr::Ident {
|
|
343
|
+
name: right_name, ..
|
|
344
|
+
},
|
|
345
|
+
) = (left.as_ref(), right.as_ref())
|
|
332
346
|
{
|
|
333
347
|
if left_name.as_ref() == param_a && right_name.as_ref() == param_b {
|
|
334
348
|
return Some(true);
|
|
@@ -360,7 +374,10 @@ impl<'a> Compiler<'a> {
|
|
|
360
374
|
ArrowBody::Expr(e) => e.as_ref(),
|
|
361
375
|
ArrowBody::Block(stmt) => {
|
|
362
376
|
let s = stmt.as_ref();
|
|
363
|
-
if let Statement::Return {
|
|
377
|
+
if let Statement::Return {
|
|
378
|
+
value: Some(ref e), ..
|
|
379
|
+
} = s
|
|
380
|
+
{
|
|
364
381
|
e
|
|
365
382
|
} else if let Statement::ExprStmt { expr: ref e, .. } = s {
|
|
366
383
|
e
|
|
@@ -376,9 +393,14 @@ impl<'a> Compiler<'a> {
|
|
|
376
393
|
}
|
|
377
394
|
}
|
|
378
395
|
// Binary: x op const or const op x
|
|
379
|
-
if let Expr::Binary {
|
|
380
|
-
|
|
381
|
-
|
|
396
|
+
if let Expr::Binary {
|
|
397
|
+
left, op, right, ..
|
|
398
|
+
} = expr_ref
|
|
399
|
+
{
|
|
400
|
+
let left_is_param =
|
|
401
|
+
matches!(left.as_ref(), Expr::Ident { name, .. } if name.as_ref() == param_name);
|
|
402
|
+
let right_is_param =
|
|
403
|
+
matches!(right.as_ref(), Expr::Ident { name, .. } if name.as_ref() == param_name);
|
|
382
404
|
let left_is_literal = matches!(left.as_ref(), Expr::Literal { .. });
|
|
383
405
|
let right_is_literal = matches!(right.as_ref(), Expr::Literal { .. });
|
|
384
406
|
if left_is_param && right_is_literal {
|
|
@@ -412,7 +434,10 @@ impl<'a> Compiler<'a> {
|
|
|
412
434
|
ArrowBody::Expr(e) => e.as_ref(),
|
|
413
435
|
ArrowBody::Block(stmt) => {
|
|
414
436
|
let s = stmt.as_ref();
|
|
415
|
-
if let Statement::Return {
|
|
437
|
+
if let Statement::Return {
|
|
438
|
+
value: Some(ref e), ..
|
|
439
|
+
} = s
|
|
440
|
+
{
|
|
416
441
|
e
|
|
417
442
|
} else if let Statement::ExprStmt { expr: ref e, .. } = s {
|
|
418
443
|
e
|
|
@@ -421,12 +446,29 @@ impl<'a> Compiler<'a> {
|
|
|
421
446
|
}
|
|
422
447
|
}
|
|
423
448
|
};
|
|
424
|
-
if let Expr::Binary {
|
|
425
|
-
|
|
449
|
+
if let Expr::Binary {
|
|
450
|
+
left, op, right, ..
|
|
451
|
+
} = expr_ref
|
|
452
|
+
{
|
|
453
|
+
if !matches!(
|
|
454
|
+
op,
|
|
455
|
+
BinOp::Eq
|
|
456
|
+
| BinOp::Ne
|
|
457
|
+
| BinOp::StrictEq
|
|
458
|
+
| BinOp::StrictNe
|
|
459
|
+
| BinOp::Lt
|
|
460
|
+
| BinOp::Le
|
|
461
|
+
| BinOp::Gt
|
|
462
|
+
| BinOp::Ge
|
|
463
|
+
| BinOp::And
|
|
464
|
+
| BinOp::Or
|
|
465
|
+
) {
|
|
426
466
|
return None;
|
|
427
467
|
}
|
|
428
|
-
let left_is_param =
|
|
429
|
-
|
|
468
|
+
let left_is_param =
|
|
469
|
+
matches!(left.as_ref(), Expr::Ident { name, .. } if name.as_ref() == param_name);
|
|
470
|
+
let right_is_param =
|
|
471
|
+
matches!(right.as_ref(), Expr::Ident { name, .. } if name.as_ref() == param_name);
|
|
430
472
|
let left_is_literal = matches!(left.as_ref(), Expr::Literal { .. });
|
|
431
473
|
let right_is_literal = matches!(right.as_ref(), Expr::Literal { .. });
|
|
432
474
|
if left_is_param && right_is_literal {
|
|
@@ -534,10 +576,9 @@ impl<'a> Compiler<'a> {
|
|
|
534
576
|
continue_patches: Vec::new(),
|
|
535
577
|
continue_is_forward_jump: false,
|
|
536
578
|
});
|
|
537
|
-
self.breakable_stack
|
|
538
|
-
.
|
|
539
|
-
|
|
540
|
-
});
|
|
579
|
+
self.breakable_stack.push(Breakable::Loop {
|
|
580
|
+
unwind_depth: self.block_depth,
|
|
581
|
+
});
|
|
541
582
|
self.compile_expr(cond)?;
|
|
542
583
|
let jump_out = self.emit_jump(Opcode::JumpIfFalse);
|
|
543
584
|
// JumpIfFalse already pops condition when taking body
|
|
@@ -580,10 +621,9 @@ impl<'a> Compiler<'a> {
|
|
|
580
621
|
continue_patches: Vec::new(),
|
|
581
622
|
continue_is_forward_jump: true,
|
|
582
623
|
});
|
|
583
|
-
self.breakable_stack
|
|
584
|
-
.
|
|
585
|
-
|
|
586
|
-
});
|
|
624
|
+
self.breakable_stack.push(Breakable::Loop {
|
|
625
|
+
unwind_depth: self.block_depth,
|
|
626
|
+
});
|
|
587
627
|
self.compile_statement(body)?;
|
|
588
628
|
let update_start = self.chunk.code.len();
|
|
589
629
|
if let Some(u) = update {
|
|
@@ -604,7 +644,12 @@ impl<'a> Compiler<'a> {
|
|
|
604
644
|
self.breakable_stack.pop();
|
|
605
645
|
self.scope.pop();
|
|
606
646
|
}
|
|
607
|
-
Statement::ForOf {
|
|
647
|
+
Statement::ForOf {
|
|
648
|
+
name,
|
|
649
|
+
iterable,
|
|
650
|
+
body,
|
|
651
|
+
..
|
|
652
|
+
} => {
|
|
608
653
|
self.compile_expr(iterable)?;
|
|
609
654
|
self.scope.push(HashMap::new());
|
|
610
655
|
let arr_name = Arc::from("__forof_arr__");
|
|
@@ -615,12 +660,18 @@ impl<'a> Compiler<'a> {
|
|
|
615
660
|
let len_idx = self.name_idx(&len_name);
|
|
616
661
|
let name_idx = self.name_idx(name);
|
|
617
662
|
self.emit_u16(Opcode::DeclareVar, arr_idx);
|
|
618
|
-
self.scope
|
|
663
|
+
self.scope
|
|
664
|
+
.last_mut()
|
|
665
|
+
.unwrap()
|
|
666
|
+
.insert(arr_name.clone(), false);
|
|
619
667
|
self.emit_u16(Opcode::LoadVar, arr_idx);
|
|
620
668
|
let len_name_idx = self.name_idx(&Arc::from("length"));
|
|
621
669
|
self.emit_u16(Opcode::GetMember, len_name_idx);
|
|
622
670
|
self.emit_u16(Opcode::DeclareVar, len_idx);
|
|
623
|
-
self.scope
|
|
671
|
+
self.scope
|
|
672
|
+
.last_mut()
|
|
673
|
+
.unwrap()
|
|
674
|
+
.insert(len_name.clone(), false);
|
|
624
675
|
let zero_idx = self.constant_idx(Constant::Number(0.0));
|
|
625
676
|
self.emit(Opcode::LoadConst);
|
|
626
677
|
self.chunk.write_u16(zero_idx);
|
|
@@ -632,15 +683,17 @@ impl<'a> Compiler<'a> {
|
|
|
632
683
|
continue_patches: Vec::new(),
|
|
633
684
|
continue_is_forward_jump: false,
|
|
634
685
|
});
|
|
635
|
-
self.breakable_stack
|
|
636
|
-
.
|
|
637
|
-
|
|
638
|
-
});
|
|
686
|
+
self.breakable_stack.push(Breakable::Loop {
|
|
687
|
+
unwind_depth: self.block_depth,
|
|
688
|
+
});
|
|
639
689
|
self.emit_u16(Opcode::LoadVar, arr_idx);
|
|
640
690
|
self.emit_u16(Opcode::LoadVar, i_idx);
|
|
641
691
|
self.emit(Opcode::GetIndex);
|
|
642
692
|
self.emit_u16(Opcode::DeclareVar, name_idx);
|
|
643
|
-
self.scope
|
|
693
|
+
self.scope
|
|
694
|
+
.last_mut()
|
|
695
|
+
.unwrap()
|
|
696
|
+
.insert(Arc::clone(name), false);
|
|
644
697
|
self.compile_statement(body)?;
|
|
645
698
|
self.emit_u16(Opcode::LoadVar, i_idx);
|
|
646
699
|
let one_idx = self.constant_idx(Constant::Number(1.0));
|
|
@@ -678,9 +731,8 @@ impl<'a> Compiler<'a> {
|
|
|
678
731
|
}
|
|
679
732
|
Statement::Break { .. } => {
|
|
680
733
|
let unwind_depth = match self.breakable_stack.last() {
|
|
681
|
-
Some(Breakable::Loop { unwind_depth })
|
|
682
|
-
|
|
683
|
-
}
|
|
734
|
+
Some(Breakable::Loop { unwind_depth })
|
|
735
|
+
| Some(Breakable::Switch { unwind_depth }) => *unwind_depth,
|
|
684
736
|
None => {
|
|
685
737
|
return Err(CompileError {
|
|
686
738
|
message: "break not inside a loop or switch".to_string(),
|
|
@@ -694,7 +746,11 @@ impl<'a> Compiler<'a> {
|
|
|
694
746
|
self.loop_stack.last_mut().unwrap().break_patches.push(pos);
|
|
695
747
|
}
|
|
696
748
|
Some(Breakable::Switch { .. }) => {
|
|
697
|
-
self.switch_stack
|
|
749
|
+
self.switch_stack
|
|
750
|
+
.last_mut()
|
|
751
|
+
.unwrap()
|
|
752
|
+
.break_patches
|
|
753
|
+
.push(pos);
|
|
698
754
|
}
|
|
699
755
|
None => {}
|
|
700
756
|
}
|
|
@@ -764,7 +820,10 @@ impl<'a> Compiler<'a> {
|
|
|
764
820
|
self.chunk.write_u16(idx);
|
|
765
821
|
let idx = self.name_idx(name);
|
|
766
822
|
self.emit_u16(Opcode::DeclareVar, idx);
|
|
767
|
-
self.scope
|
|
823
|
+
self.scope
|
|
824
|
+
.last_mut()
|
|
825
|
+
.unwrap()
|
|
826
|
+
.insert(Arc::clone(name), false);
|
|
768
827
|
}
|
|
769
828
|
Statement::DoWhile { body, cond, .. } => {
|
|
770
829
|
let start = self.chunk.code.len();
|
|
@@ -773,10 +832,9 @@ impl<'a> Compiler<'a> {
|
|
|
773
832
|
continue_patches: Vec::new(),
|
|
774
833
|
continue_is_forward_jump: false,
|
|
775
834
|
});
|
|
776
|
-
self.breakable_stack
|
|
777
|
-
.
|
|
778
|
-
|
|
779
|
-
});
|
|
835
|
+
self.breakable_stack.push(Breakable::Loop {
|
|
836
|
+
unwind_depth: self.block_depth,
|
|
837
|
+
});
|
|
780
838
|
self.compile_statement(body)?;
|
|
781
839
|
let cond_start = self.chunk.code.len();
|
|
782
840
|
self.compile_expr(cond)?;
|
|
@@ -794,7 +852,12 @@ impl<'a> Compiler<'a> {
|
|
|
794
852
|
self.patch_jump(p, end);
|
|
795
853
|
}
|
|
796
854
|
}
|
|
797
|
-
Statement::Switch {
|
|
855
|
+
Statement::Switch {
|
|
856
|
+
expr,
|
|
857
|
+
cases,
|
|
858
|
+
default_body,
|
|
859
|
+
..
|
|
860
|
+
} => {
|
|
798
861
|
let switch_unwind_depth = self.block_depth;
|
|
799
862
|
self.switch_stack.push(SwitchInfo {
|
|
800
863
|
break_patches: Vec::new(),
|
|
@@ -899,7 +962,8 @@ impl<'a> Compiler<'a> {
|
|
|
899
962
|
if let Some(finally) = finally_body {
|
|
900
963
|
self.compile_statement(finally)?;
|
|
901
964
|
}
|
|
902
|
-
let catch_offset =
|
|
965
|
+
let catch_offset =
|
|
966
|
+
catch_start.wrapping_sub(catch_offset_pos).wrapping_sub(3) as u16;
|
|
903
967
|
self.chunk.code[catch_offset_pos + 1] = (catch_offset >> 8) as u8;
|
|
904
968
|
self.chunk.code[catch_offset_pos + 2] = (catch_offset & 0xff) as u8;
|
|
905
969
|
}
|
|
@@ -918,6 +982,9 @@ impl<'a> Compiler<'a> {
|
|
|
918
982
|
});
|
|
919
983
|
}
|
|
920
984
|
},
|
|
985
|
+
Statement::TypeAlias { .. }
|
|
986
|
+
| Statement::DeclareVar { .. }
|
|
987
|
+
| Statement::DeclareFun { .. } => {}
|
|
921
988
|
}
|
|
922
989
|
Ok(())
|
|
923
990
|
}
|
|
@@ -937,7 +1004,7 @@ impl<'a> Compiler<'a> {
|
|
|
937
1004
|
DestructPattern::Array(elements) => {
|
|
938
1005
|
for (i, elem) in elements.iter().enumerate() {
|
|
939
1006
|
match elem {
|
|
940
|
-
Some(DestructElement::Ident(name)) => {
|
|
1007
|
+
Some(DestructElement::Ident(name, _)) => {
|
|
941
1008
|
self.emit(Opcode::Dup);
|
|
942
1009
|
let idx = self.constant_idx(Constant::Number(i as f64));
|
|
943
1010
|
self.emit(Opcode::LoadConst);
|
|
@@ -967,7 +1034,7 @@ impl<'a> Compiler<'a> {
|
|
|
967
1034
|
self.chunk.write_u16(key_idx);
|
|
968
1035
|
self.emit(Opcode::GetIndex); // GetIndex pops obj, index and uses get_member
|
|
969
1036
|
match &prop.value {
|
|
970
|
-
DestructElement::Ident(name) => {
|
|
1037
|
+
DestructElement::Ident(name, _) => {
|
|
971
1038
|
let idx = self.name_idx(name);
|
|
972
1039
|
self.emit_u16(decl_op, idx);
|
|
973
1040
|
if mutable {
|
|
@@ -1008,7 +1075,9 @@ impl<'a> Compiler<'a> {
|
|
|
1008
1075
|
let idx = self.name_idx(name);
|
|
1009
1076
|
self.emit_u16(Opcode::LoadVar, idx);
|
|
1010
1077
|
}
|
|
1011
|
-
Expr::Binary {
|
|
1078
|
+
Expr::Binary {
|
|
1079
|
+
left, op, right, ..
|
|
1080
|
+
} => {
|
|
1012
1081
|
match op {
|
|
1013
1082
|
BinOp::And => {
|
|
1014
1083
|
// Short-circuit: a && b => if !a then a else b
|
|
@@ -1049,16 +1118,29 @@ impl<'a> Compiler<'a> {
|
|
|
1049
1118
|
&& args.len() == 1
|
|
1050
1119
|
&& matches!(args[0], CallArg::Expr(_))
|
|
1051
1120
|
{
|
|
1052
|
-
if let (
|
|
1053
|
-
|
|
1121
|
+
if let (
|
|
1122
|
+
Expr::Member {
|
|
1123
|
+
object,
|
|
1124
|
+
prop: MemberProp::Name { name: key, .. },
|
|
1125
|
+
optional: false,
|
|
1126
|
+
..
|
|
1127
|
+
},
|
|
1128
|
+
CallArg::Expr(cmp_expr),
|
|
1129
|
+
) = (callee.as_ref(), &args[0])
|
|
1054
1130
|
{
|
|
1055
1131
|
if key.as_ref() == "sort" {
|
|
1056
|
-
if let Some(ascending) = Self::detect_numeric_sort_comparator(cmp_expr)
|
|
1132
|
+
if let Some(ascending) = Self::detect_numeric_sort_comparator(cmp_expr)
|
|
1133
|
+
{
|
|
1057
1134
|
self.compile_expr(object)?;
|
|
1058
|
-
self.emit_u8(
|
|
1135
|
+
self.emit_u8(
|
|
1136
|
+
Opcode::ArraySortNumeric,
|
|
1137
|
+
if ascending { 0 } else { 1 },
|
|
1138
|
+
);
|
|
1059
1139
|
return Ok(());
|
|
1060
1140
|
}
|
|
1061
|
-
if let Some((prop, ascending)) =
|
|
1141
|
+
if let Some((prop, ascending)) =
|
|
1142
|
+
Self::detect_property_sort_comparator(cmp_expr)
|
|
1143
|
+
{
|
|
1062
1144
|
self.compile_expr(object)?;
|
|
1063
1145
|
let prop_idx = self.constant_idx(Constant::String(prop));
|
|
1064
1146
|
self.emit(Opcode::ArraySortByProperty);
|
|
@@ -1152,7 +1234,7 @@ impl<'a> Compiler<'a> {
|
|
|
1152
1234
|
let jump_end = self.emit_jump(Opcode::Jump);
|
|
1153
1235
|
self.patch_jump(jump_to_get, self.chunk.code.len());
|
|
1154
1236
|
match prop {
|
|
1155
|
-
MemberProp::Name
|
|
1237
|
+
MemberProp::Name { name: key, .. } => {
|
|
1156
1238
|
let idx = self.name_idx(key);
|
|
1157
1239
|
self.emit_u16(Opcode::GetMemberOptional, idx);
|
|
1158
1240
|
}
|
|
@@ -1164,7 +1246,7 @@ impl<'a> Compiler<'a> {
|
|
|
1164
1246
|
self.patch_jump(jump_end, self.chunk.code.len());
|
|
1165
1247
|
} else {
|
|
1166
1248
|
match prop {
|
|
1167
|
-
MemberProp::Name
|
|
1249
|
+
MemberProp::Name { name: key, .. } => {
|
|
1168
1250
|
let idx = self.name_idx(key);
|
|
1169
1251
|
self.emit_u16(Opcode::GetMember, idx);
|
|
1170
1252
|
}
|
|
@@ -1211,7 +1293,9 @@ impl<'a> Compiler<'a> {
|
|
|
1211
1293
|
self.patch_jump(jump_end, self.chunk.code.len());
|
|
1212
1294
|
}
|
|
1213
1295
|
Expr::Array { elements, .. } => {
|
|
1214
|
-
let has_spread = elements
|
|
1296
|
+
let has_spread = elements
|
|
1297
|
+
.iter()
|
|
1298
|
+
.any(|e| matches!(e, ArrayElement::Spread(_)));
|
|
1215
1299
|
if has_spread {
|
|
1216
1300
|
// Build array incrementally: start with [], concat each element
|
|
1217
1301
|
self.emit_u16(Opcode::NewArray, 0);
|
|
@@ -1376,7 +1460,9 @@ impl<'a> Compiler<'a> {
|
|
|
1376
1460
|
self.emit(Opcode::Dup);
|
|
1377
1461
|
self.emit_u16(Opcode::StoreVar, idx);
|
|
1378
1462
|
}
|
|
1379
|
-
Expr::CompoundAssign {
|
|
1463
|
+
Expr::CompoundAssign {
|
|
1464
|
+
name, op, value, ..
|
|
1465
|
+
} => {
|
|
1380
1466
|
let idx = self.name_idx(name);
|
|
1381
1467
|
self.emit_u16(Opcode::LoadVar, idx);
|
|
1382
1468
|
self.compile_expr(value)?;
|
|
@@ -1384,20 +1470,32 @@ impl<'a> Compiler<'a> {
|
|
|
1384
1470
|
self.emit(Opcode::Dup);
|
|
1385
1471
|
self.emit_u16(Opcode::StoreVar, idx);
|
|
1386
1472
|
}
|
|
1387
|
-
Expr::MemberAssign {
|
|
1473
|
+
Expr::MemberAssign {
|
|
1474
|
+
object,
|
|
1475
|
+
prop,
|
|
1476
|
+
value,
|
|
1477
|
+
..
|
|
1478
|
+
} => {
|
|
1388
1479
|
self.compile_expr(object)?;
|
|
1389
1480
|
self.compile_expr(value)?;
|
|
1390
1481
|
let idx = self.name_idx(prop);
|
|
1391
1482
|
self.emit_u16(Opcode::SetMember, idx); // SetMember pops obj, val and pushes val back
|
|
1392
1483
|
}
|
|
1393
|
-
Expr::IndexAssign {
|
|
1484
|
+
Expr::IndexAssign {
|
|
1485
|
+
object,
|
|
1486
|
+
index,
|
|
1487
|
+
value,
|
|
1488
|
+
..
|
|
1489
|
+
} => {
|
|
1394
1490
|
self.compile_expr(object)?;
|
|
1395
1491
|
self.compile_expr(index)?;
|
|
1396
1492
|
self.compile_expr(value)?;
|
|
1397
1493
|
self.emit(Opcode::Dup); // leave copy for assignment expression result
|
|
1398
1494
|
self.emit(Opcode::SetIndex);
|
|
1399
1495
|
}
|
|
1400
|
-
Expr::NativeModuleLoad {
|
|
1496
|
+
Expr::NativeModuleLoad {
|
|
1497
|
+
spec, export_name, ..
|
|
1498
|
+
} => {
|
|
1401
1499
|
let spec_idx = self.constant_idx(Constant::String(Arc::clone(spec)));
|
|
1402
1500
|
let export_idx = self.constant_idx(Constant::String(Arc::clone(export_name)));
|
|
1403
1501
|
self.emit(Opcode::LoadNativeExport);
|
|
@@ -1405,7 +1503,10 @@ impl<'a> Compiler<'a> {
|
|
|
1405
1503
|
self.chunk.write_u16(export_idx);
|
|
1406
1504
|
}
|
|
1407
1505
|
Expr::JsxElement {
|
|
1408
|
-
tag,
|
|
1506
|
+
tag,
|
|
1507
|
+
props,
|
|
1508
|
+
children,
|
|
1509
|
+
..
|
|
1409
1510
|
} => {
|
|
1410
1511
|
self.compile_jsx_element(tag, props, children)?;
|
|
1411
1512
|
}
|
|
@@ -1413,16 +1514,13 @@ impl<'a> Compiler<'a> {
|
|
|
1413
1514
|
self.compile_jsx_fragment(children)?;
|
|
1414
1515
|
}
|
|
1415
1516
|
Expr::Await { operand, .. } => {
|
|
1416
|
-
// await expr =>
|
|
1417
|
-
let spec_idx = self.constant_idx(Constant::String(Arc::from("tish:http")));
|
|
1418
|
-
let await_idx = self.constant_idx(Constant::String(Arc::from("await")));
|
|
1419
|
-
self.emit(Opcode::LoadNativeExport);
|
|
1420
|
-
self.chunk.write_u16(spec_idx);
|
|
1421
|
-
self.chunk.write_u16(await_idx);
|
|
1517
|
+
// await expr => evaluate operand, then VM Opcode::AwaitPromise (throw on reject).
|
|
1422
1518
|
self.compile_expr(operand)?;
|
|
1423
|
-
self.
|
|
1519
|
+
self.emit(Opcode::AwaitPromise);
|
|
1424
1520
|
}
|
|
1425
|
-
Expr::LogicalAssign {
|
|
1521
|
+
Expr::LogicalAssign {
|
|
1522
|
+
name, op, value, ..
|
|
1523
|
+
} => {
|
|
1426
1524
|
let idx = self.name_idx(name);
|
|
1427
1525
|
match op {
|
|
1428
1526
|
LogicalAssignOp::OrOr => {
|
|
@@ -1515,7 +1613,11 @@ impl<'a> Compiler<'a> {
|
|
|
1515
1613
|
let h_idx = self.name_idx(&Arc::from("h"));
|
|
1516
1614
|
self.emit_u16(Opcode::LoadGlobal, h_idx);
|
|
1517
1615
|
let tag_str = tag.as_ref();
|
|
1518
|
-
let is_component = tag_str
|
|
1616
|
+
let is_component = tag_str
|
|
1617
|
+
.chars()
|
|
1618
|
+
.next()
|
|
1619
|
+
.map(|c| c.is_uppercase())
|
|
1620
|
+
.unwrap_or(false);
|
|
1519
1621
|
if is_component {
|
|
1520
1622
|
let tag_idx = self.name_idx(tag);
|
|
1521
1623
|
self.emit_u16(Opcode::LoadGlobal, tag_idx);
|
|
@@ -93,13 +93,16 @@ pub enum Opcode {
|
|
|
93
93
|
ExitBlock = 41,
|
|
94
94
|
/// Like [`DeclareVar`] but does not record block-scope undo (for `for`/`for-of` header bindings).
|
|
95
95
|
DeclareVarPlain = 42,
|
|
96
|
+
/// Pop the `await` operand value; if it is a `Promise`, block until settled, push the result,
|
|
97
|
+
/// or unwind to `catch` like `Throw` on rejection.
|
|
98
|
+
AwaitPromise = 43,
|
|
96
99
|
}
|
|
97
100
|
|
|
98
101
|
impl Opcode {
|
|
99
|
-
/// Decode byte to opcode. Safe for b in 0..=
|
|
102
|
+
/// Decode byte to opcode. Safe for b in 0..=43 (matches #[repr(u8)] discriminants).
|
|
100
103
|
#[inline]
|
|
101
104
|
pub fn from_u8(b: u8) -> Option<Opcode> {
|
|
102
|
-
if b <=
|
|
105
|
+
if b <= 43 {
|
|
103
106
|
Some(unsafe { std::mem::transmute(b) })
|
|
104
107
|
} else {
|
|
105
108
|
None
|
|
@@ -109,10 +112,25 @@ impl Opcode {
|
|
|
109
112
|
/// Size in bytes of this instruction at `ip` (including operands). Returns None if truncated.
|
|
110
113
|
pub fn instruction_size(self, code: &[u8], ip: usize) -> Option<usize> {
|
|
111
114
|
let size = match self {
|
|
112
|
-
Opcode::Nop
|
|
113
|
-
| Opcode::
|
|
114
|
-
| Opcode::
|
|
115
|
-
|
|
115
|
+
Opcode::Nop
|
|
116
|
+
| Opcode::Pop
|
|
117
|
+
| Opcode::Dup
|
|
118
|
+
| Opcode::Return
|
|
119
|
+
| Opcode::ExitTry
|
|
120
|
+
| Opcode::ConcatArray
|
|
121
|
+
| Opcode::MergeObject
|
|
122
|
+
| Opcode::GetIndex
|
|
123
|
+
| Opcode::SetIndex
|
|
124
|
+
| Opcode::Throw
|
|
125
|
+
| Opcode::ArrayMapIdentity
|
|
126
|
+
| Opcode::CallSpread
|
|
127
|
+
| Opcode::ConstructSpread
|
|
128
|
+
| Opcode::EnterBlock
|
|
129
|
+
| Opcode::ExitBlock
|
|
130
|
+
| Opcode::AwaitPromise => 1,
|
|
131
|
+
Opcode::ArraySortByProperty
|
|
132
|
+
| Opcode::ArrayMapBinOp
|
|
133
|
+
| Opcode::ArrayFilterBinOp
|
|
116
134
|
| Opcode::LoadNativeExport => 5,
|
|
117
135
|
_ => 3,
|
|
118
136
|
};
|
|
@@ -162,8 +162,8 @@ fn chain_jumps(code: &mut [u8]) {
|
|
|
162
162
|
let current_offset = read_i16(code, ip + 1) as isize;
|
|
163
163
|
let current_target = (ip as isize + 3 + current_offset).max(0) as usize;
|
|
164
164
|
if let Some(final_target) = final_jump_target(code, ip) {
|
|
165
|
-
let target_ok =
|
|
166
|
-
|| insn_starts.contains(&final_target);
|
|
165
|
+
let target_ok =
|
|
166
|
+
final_target == code.len() || insn_starts.contains(&final_target);
|
|
167
167
|
if final_target != current_target && target_ok {
|
|
168
168
|
let new_offset = final_target as i32 - (ip + 3) as i32;
|
|
169
169
|
if (i16::MIN as i32..=i16::MAX as i32).contains(&new_offset) {
|