@tishlang/tish-format 1.0.12 → 2.0.1
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 +51 -0
- package/LICENSE +13 -0
- package/bin/tish-format +0 -0
- package/crates/js_to_tish/Cargo.toml +11 -0
- package/crates/js_to_tish/README.md +18 -0
- package/crates/js_to_tish/src/error.rs +55 -0
- package/crates/js_to_tish/src/lib.rs +11 -0
- package/crates/js_to_tish/src/span_util.rs +35 -0
- package/crates/js_to_tish/src/transform/expr.rs +611 -0
- package/crates/js_to_tish/src/transform/stmt.rs +503 -0
- package/crates/js_to_tish/src/transform.rs +60 -0
- package/crates/tish/Cargo.toml +62 -0
- package/crates/tish/build.rs +21 -0
- package/crates/tish/src/cargo_native_registry.rs +32 -0
- package/crates/tish/src/cli_help.rs +576 -0
- package/crates/tish/src/main.rs +853 -0
- package/crates/tish/src/repl_completion.rs +199 -0
- package/crates/tish/tests/cargo_example_compile.rs +67 -0
- package/crates/tish/tests/error_source_location.rs +36 -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/fixtures/runtime_error_location.tish +5 -0
- package/crates/tish/tests/fixtures/trycatch_runtime_errors.tish +15 -0
- package/crates/tish/tests/fixtures/tty_capability.tish +9 -0
- package/crates/tish/tests/integration_test.rs +1406 -0
- package/crates/tish/tests/run_optimize_stdout_parity.rs +50 -0
- package/crates/tish/tests/shortcircuit.rs +65 -0
- package/crates/tish/tests/trycatch_runtime_errors.rs +45 -0
- package/crates/tish/tests/tty_capability.rs +43 -0
- package/crates/tish_ast/Cargo.toml +9 -0
- package/crates/tish_ast/src/ast.rs +649 -0
- package/crates/tish_ast/src/lib.rs +5 -0
- package/crates/tish_build_utils/Cargo.toml +11 -0
- package/crates/tish_build_utils/src/lib.rs +577 -0
- package/crates/tish_builtins/Cargo.toml +22 -0
- package/crates/tish_builtins/src/array.rs +803 -0
- package/crates/tish_builtins/src/collections.rs +481 -0
- package/crates/tish_builtins/src/construct.rs +199 -0
- package/crates/tish_builtins/src/date.rs +538 -0
- package/crates/tish_builtins/src/globals.rs +293 -0
- package/crates/tish_builtins/src/helpers.rs +35 -0
- package/crates/tish_builtins/src/iterator.rs +129 -0
- package/crates/tish_builtins/src/lib.rs +21 -0
- package/crates/tish_builtins/src/math.rs +89 -0
- package/crates/tish_builtins/src/number.rs +96 -0
- package/crates/tish_builtins/src/object.rs +36 -0
- package/crates/tish_builtins/src/string.rs +646 -0
- package/crates/tish_builtins/src/symbol.rs +83 -0
- package/crates/tish_builtins/src/typedarrays.rs +298 -0
- package/crates/tish_bytecode/Cargo.toml +17 -0
- package/crates/tish_bytecode/src/chunk.rs +164 -0
- package/crates/tish_bytecode/src/compiler.rs +2604 -0
- package/crates/tish_bytecode/src/encoding.rs +102 -0
- package/crates/tish_bytecode/src/lib.rs +20 -0
- package/crates/tish_bytecode/src/opcode.rs +185 -0
- package/crates/tish_bytecode/src/peephole.rs +189 -0
- package/crates/tish_bytecode/src/serialize.rs +193 -0
- package/crates/tish_bytecode/tests/break_continue_bytecode.rs +44 -0
- package/crates/tish_bytecode/tests/constant_folding.rs +84 -0
- package/crates/tish_bytecode/tests/sort_optimization.rs +31 -0
- package/crates/tish_compile/Cargo.toml +27 -0
- package/crates/tish_compile/src/check.rs +774 -0
- package/crates/tish_compile/src/codegen.rs +7317 -0
- package/crates/tish_compile/src/infer.rs +1681 -0
- package/crates/tish_compile/src/lib.rs +206 -0
- package/crates/tish_compile/src/resolve.rs +1951 -0
- package/crates/tish_compile/src/types.rs +605 -0
- package/crates/tish_compile_js/Cargo.toml +18 -0
- package/crates/tish_compile_js/examples/jsx_vdom_smoke.tish +8 -0
- package/crates/tish_compile_js/src/codegen.rs +938 -0
- package/crates/tish_compile_js/src/error.rs +20 -0
- package/crates/tish_compile_js/src/lib.rs +26 -0
- package/crates/tish_compile_js/src/tests_jsx.rs +414 -0
- package/crates/tish_compiler_wasm/Cargo.toml +21 -0
- package/crates/tish_compiler_wasm/src/lib.rs +57 -0
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +473 -0
- package/crates/tish_core/Cargo.toml +32 -0
- package/crates/tish_core/src/console_style.rs +170 -0
- package/crates/tish_core/src/json.rs +430 -0
- package/crates/tish_core/src/lib.rs +20 -0
- package/crates/tish_core/src/macros.rs +36 -0
- package/crates/tish_core/src/shape.rs +85 -0
- package/crates/tish_core/src/uri.rs +118 -0
- package/crates/tish_core/src/value.rs +1350 -0
- package/crates/tish_core/src/vmref.rs +183 -0
- package/crates/tish_cranelift/Cargo.toml +19 -0
- package/crates/tish_cranelift/src/lib.rs +43 -0
- package/crates/tish_cranelift/src/link.rs +130 -0
- package/crates/tish_cranelift/src/lower.rs +85 -0
- package/crates/tish_cranelift_runtime/Cargo.toml +26 -0
- package/crates/tish_cranelift_runtime/src/lib.rs +45 -0
- package/crates/tish_eval/Cargo.toml +51 -0
- package/crates/tish_eval/src/eval.rs +4265 -0
- package/crates/tish_eval/src/http.rs +191 -0
- package/crates/tish_eval/src/lib.rs +99 -0
- package/crates/tish_eval/src/natives.rs +551 -0
- package/crates/tish_eval/src/promise.rs +179 -0
- package/crates/tish_eval/src/regex.rs +299 -0
- package/crates/tish_eval/src/timers.rs +120 -0
- package/crates/tish_eval/src/value.rs +336 -0
- package/crates/tish_eval/src/value_convert.rs +117 -0
- package/crates/tish_ffi/Cargo.toml +26 -0
- package/crates/tish_ffi/src/lib.rs +518 -0
- package/crates/tish_ffi/tests/fixtures/testmod/Cargo.toml +18 -0
- package/crates/tish_ffi/tests/fixtures/testmod/src/lib.rs +46 -0
- package/crates/tish_ffi/tests/loader.rs +65 -0
- package/crates/tish_fmt/Cargo.toml +16 -0
- package/crates/tish_fmt/src/bin/tish-fmt.rs +41 -0
- package/crates/tish_fmt/src/lib.rs +2157 -0
- package/crates/tish_jsx_web/Cargo.toml +9 -0
- package/crates/tish_jsx_web/README.md +5 -0
- package/crates/tish_jsx_web/src/lib.rs +2 -0
- package/crates/tish_lexer/Cargo.toml +9 -0
- package/crates/tish_lexer/src/lib.rs +1104 -0
- package/crates/tish_lexer/src/token.rs +170 -0
- package/crates/tish_lint/Cargo.toml +18 -0
- package/crates/tish_lint/src/bin/tish-lint.rs +195 -0
- package/crates/tish_lint/src/lib.rs +281 -0
- package/crates/tish_llvm/Cargo.toml +13 -0
- package/crates/tish_llvm/src/lib.rs +115 -0
- package/crates/tish_lsp/Cargo.toml +25 -0
- package/crates/tish_lsp/README.md +26 -0
- package/crates/tish_lsp/src/builtin_goto.rs +362 -0
- package/crates/tish_lsp/src/import_goto.rs +564 -0
- package/crates/tish_lsp/src/main.rs +1459 -0
- package/crates/tish_native/Cargo.toml +16 -0
- package/crates/tish_native/src/build.rs +481 -0
- package/crates/tish_native/src/config.rs +48 -0
- package/crates/tish_native/src/lib.rs +416 -0
- package/crates/tish_opt/Cargo.toml +13 -0
- package/crates/tish_opt/src/lib.rs +1046 -0
- package/crates/tish_parser/Cargo.toml +11 -0
- package/crates/tish_parser/src/lib.rs +386 -0
- package/crates/tish_parser/src/parser.rs +2726 -0
- package/crates/tish_pg/Cargo.toml +34 -0
- package/crates/tish_pg/README.md +38 -0
- package/crates/tish_pg/src/error.rs +52 -0
- package/crates/tish_pg/src/lib.rs +955 -0
- package/crates/tish_resolve/Cargo.toml +13 -0
- package/crates/tish_resolve/src/lib.rs +3601 -0
- package/crates/tish_resolve/src/pos.rs +141 -0
- package/crates/tish_runtime/Cargo.toml +100 -0
- package/crates/tish_runtime/src/http.rs +1347 -0
- package/crates/tish_runtime/src/http_fetch.rs +492 -0
- package/crates/tish_runtime/src/http_hyper.rs +441 -0
- package/crates/tish_runtime/src/http_prefork.rs +189 -0
- package/crates/tish_runtime/src/lib.rs +1447 -0
- package/crates/tish_runtime/src/native_promise.rs +15 -0
- package/crates/tish_runtime/src/promise.rs +558 -0
- package/crates/tish_runtime/src/promise_io.rs +38 -0
- package/crates/tish_runtime/src/timers.rs +172 -0
- package/crates/tish_runtime/src/tty.rs +226 -0
- package/crates/tish_runtime/src/ws.rs +778 -0
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +102 -0
- package/crates/tish_ui/Cargo.toml +17 -0
- package/crates/tish_ui/src/jsx.rs +692 -0
- package/crates/tish_ui/src/lib.rs +20 -0
- package/crates/tish_ui/src/runtime/hooks.rs +573 -0
- package/crates/tish_ui/src/runtime/mod.rs +183 -0
- package/crates/tish_vm/Cargo.toml +60 -0
- package/crates/tish_vm/src/jit.rs +1050 -0
- package/crates/tish_vm/src/lib.rs +41 -0
- package/crates/tish_vm/src/vm.rs +3536 -0
- package/crates/tish_vm/tests/concurrent_shared_state.rs +140 -0
- package/crates/tish_vm/tests/fixtures/or_string_cmd.tish +2 -0
- package/crates/tish_vm/tests/lexical_scope_declare.rs +34 -0
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +150 -0
- package/crates/tish_wasm/Cargo.toml +15 -0
- package/crates/tish_wasm/src/lib.rs +428 -0
- package/crates/tish_wasm_runtime/Cargo.toml +37 -0
- package/crates/tish_wasm_runtime/src/gpu.rs +429 -0
- package/crates/tish_wasm_runtime/src/lib.rs +42 -0
- package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +261 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +125 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +382 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +167 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +117 -0
- package/justfile +276 -0
- package/package.json +2 -2
- package/platform/darwin-arm64/tish-fmt +0 -0
- package/platform/darwin-x64/tish-fmt +0 -0
- package/platform/linux-arm64/tish-fmt +0 -0
- package/platform/linux-x64/tish-fmt +0 -0
- package/platform/win32-x64/tish-fmt.exe +0 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
//! Canonical u8 encoding for AST operators in bytecode.
|
|
2
|
+
//! Single source of truth: compiler encodes with *\_to_u8, VM decodes with u8_to\_*.
|
|
3
|
+
|
|
4
|
+
use tishlang_ast::{BinOp, CompoundOp, UnaryOp};
|
|
5
|
+
|
|
6
|
+
/// Encode BinOp for bytecode operand. Used by compiler.
|
|
7
|
+
pub fn binop_to_u8(op: BinOp) -> u8 {
|
|
8
|
+
use tishlang_ast::BinOp::*;
|
|
9
|
+
match op {
|
|
10
|
+
Add => 0,
|
|
11
|
+
Sub => 1,
|
|
12
|
+
Mul => 2,
|
|
13
|
+
Div => 3,
|
|
14
|
+
Mod => 4,
|
|
15
|
+
Pow => 5,
|
|
16
|
+
Eq => 6,
|
|
17
|
+
Ne => 7,
|
|
18
|
+
StrictEq => 8,
|
|
19
|
+
StrictNe => 9,
|
|
20
|
+
Lt => 10,
|
|
21
|
+
Le => 11,
|
|
22
|
+
Gt => 12,
|
|
23
|
+
Ge => 13,
|
|
24
|
+
And => 14,
|
|
25
|
+
Or => 15,
|
|
26
|
+
BitAnd => 16,
|
|
27
|
+
BitOr => 17,
|
|
28
|
+
BitXor => 18,
|
|
29
|
+
Shl => 19,
|
|
30
|
+
Shr => 20,
|
|
31
|
+
In => 21,
|
|
32
|
+
UShr => 22,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Decode bytecode operand to BinOp. Used by VM.
|
|
37
|
+
pub fn u8_to_binop(b: u8) -> Option<BinOp> {
|
|
38
|
+
use tishlang_ast::BinOp::*;
|
|
39
|
+
Some(match b {
|
|
40
|
+
0 => Add,
|
|
41
|
+
1 => Sub,
|
|
42
|
+
2 => Mul,
|
|
43
|
+
3 => Div,
|
|
44
|
+
4 => Mod,
|
|
45
|
+
5 => Pow,
|
|
46
|
+
6 => Eq,
|
|
47
|
+
7 => Ne,
|
|
48
|
+
8 => StrictEq,
|
|
49
|
+
9 => StrictNe,
|
|
50
|
+
10 => Lt,
|
|
51
|
+
11 => Le,
|
|
52
|
+
12 => Gt,
|
|
53
|
+
13 => Ge,
|
|
54
|
+
14 => And,
|
|
55
|
+
15 => Or,
|
|
56
|
+
16 => BitAnd,
|
|
57
|
+
17 => BitOr,
|
|
58
|
+
18 => BitXor,
|
|
59
|
+
19 => Shl,
|
|
60
|
+
20 => Shr,
|
|
61
|
+
21 => In,
|
|
62
|
+
22 => UShr,
|
|
63
|
+
_ => return None,
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// Encode CompoundOp for bytecode (same numeric subset as BinOp: Add,Sub,Mul,Div,Mod).
|
|
68
|
+
pub fn compound_op_to_u8(op: CompoundOp) -> u8 {
|
|
69
|
+
use tishlang_ast::CompoundOp::*;
|
|
70
|
+
match op {
|
|
71
|
+
Add => 0,
|
|
72
|
+
Sub => 1,
|
|
73
|
+
Mul => 2,
|
|
74
|
+
Div => 3,
|
|
75
|
+
Mod => 4,
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// Encode UnaryOp for bytecode operand. Used by compiler.
|
|
80
|
+
pub fn unaryop_to_u8(op: UnaryOp) -> u8 {
|
|
81
|
+
use tishlang_ast::UnaryOp::*;
|
|
82
|
+
match op {
|
|
83
|
+
Not => 0,
|
|
84
|
+
Neg => 1,
|
|
85
|
+
Pos => 2,
|
|
86
|
+
BitNot => 3,
|
|
87
|
+
Void => 4,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/// Decode bytecode operand to UnaryOp. Used by VM.
|
|
92
|
+
pub fn u8_to_unaryop(b: u8) -> Option<UnaryOp> {
|
|
93
|
+
use tishlang_ast::UnaryOp::*;
|
|
94
|
+
Some(match b {
|
|
95
|
+
0 => Not,
|
|
96
|
+
1 => Neg,
|
|
97
|
+
2 => Pos,
|
|
98
|
+
3 => BitNot,
|
|
99
|
+
4 => Void,
|
|
100
|
+
_ => return None,
|
|
101
|
+
})
|
|
102
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//! Bytecode compiler for Tish.
|
|
2
|
+
//! Compiles AST to stack-based bytecode for VM execution.
|
|
3
|
+
|
|
4
|
+
mod chunk;
|
|
5
|
+
mod compiler;
|
|
6
|
+
mod encoding;
|
|
7
|
+
mod opcode;
|
|
8
|
+
mod peephole;
|
|
9
|
+
mod serialize;
|
|
10
|
+
|
|
11
|
+
pub const NO_REST_PARAM: u16 = 0xFFFF;
|
|
12
|
+
|
|
13
|
+
pub use chunk::{Chunk, Constant};
|
|
14
|
+
pub use compiler::{
|
|
15
|
+
compile, compile_for_repl, compile_for_repl_unoptimized, compile_unoptimized,
|
|
16
|
+
compile_with_source, CompileError,
|
|
17
|
+
};
|
|
18
|
+
pub use encoding::{binop_to_u8, compound_op_to_u8, u8_to_binop, u8_to_unaryop, unaryop_to_u8};
|
|
19
|
+
pub use opcode::Opcode;
|
|
20
|
+
pub use serialize::{deserialize, serialize};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
//! Bytecode opcodes for the Tish VM.
|
|
2
|
+
|
|
3
|
+
/// Stack-based bytecode opcodes.
|
|
4
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
5
|
+
#[repr(u8)]
|
|
6
|
+
pub enum Opcode {
|
|
7
|
+
/// No operation
|
|
8
|
+
Nop = 0,
|
|
9
|
+
/// Push constant from constants table (operand: u16 index)
|
|
10
|
+
LoadConst = 1,
|
|
11
|
+
/// Load variable from scope (operand: u16 name index)
|
|
12
|
+
LoadVar = 2,
|
|
13
|
+
/// Store top of stack to variable (operand: u16 name index)
|
|
14
|
+
StoreVar = 3,
|
|
15
|
+
/// Discard top of stack
|
|
16
|
+
Pop = 4,
|
|
17
|
+
/// Duplicate top of stack
|
|
18
|
+
Dup = 5,
|
|
19
|
+
/// Call function with n args (operand: u16 arg count). Callee and args on stack.
|
|
20
|
+
Call = 6,
|
|
21
|
+
/// Return from function. Top of stack is return value.
|
|
22
|
+
Return = 7,
|
|
23
|
+
/// Unconditional jump forward (operand: u16 byte offset)
|
|
24
|
+
Jump = 8,
|
|
25
|
+
/// Pop top; if falsy, jump forward (operand: u16 byte offset)
|
|
26
|
+
JumpIfFalse = 9,
|
|
27
|
+
/// Unconditional jump backward (operand: u16 byte offset)
|
|
28
|
+
JumpBack = 10,
|
|
29
|
+
/// Binary operation (operand: u8 BinOp variant). Pops left, right; pushes result.
|
|
30
|
+
BinOp = 11,
|
|
31
|
+
/// Unary operation (operand: u8 UnaryOp variant). Pops operand; pushes result.
|
|
32
|
+
UnaryOp = 12,
|
|
33
|
+
/// Get property: obj.prop (operand: u16 prop name index). Pops obj; pushes value.
|
|
34
|
+
GetMember = 13,
|
|
35
|
+
/// Set property: obj.prop = val (operand: u16 prop name index). Pops obj, val.
|
|
36
|
+
SetMember = 14,
|
|
37
|
+
/// Get index: obj[idx]. Pops obj, idx; pushes value.
|
|
38
|
+
GetIndex = 15,
|
|
39
|
+
/// Set index: obj[idx] = val. Pops obj, idx, val.
|
|
40
|
+
SetIndex = 16,
|
|
41
|
+
/// Create array with n elements (operand: u16 count). Elements on stack.
|
|
42
|
+
NewArray = 17,
|
|
43
|
+
/// Create object with n key-value pairs (operand: u16 count). Keys and vals interleaved.
|
|
44
|
+
NewObject = 18,
|
|
45
|
+
/// Load from global scope (operand: u16 name index)
|
|
46
|
+
LoadGlobal = 19,
|
|
47
|
+
/// Store to global scope (operand: u16 name index)
|
|
48
|
+
StoreGlobal = 20,
|
|
49
|
+
/// Create closure: push function (operand: u16 chunk index for nested function)
|
|
50
|
+
Closure = 21,
|
|
51
|
+
/// Pop and discard n values (operand: u16 count)
|
|
52
|
+
PopN = 22,
|
|
53
|
+
/// Load `this` or receiver (for method calls)
|
|
54
|
+
LoadThis = 23,
|
|
55
|
+
/// Throw: pop value, unwind to catch handler, push value, jump
|
|
56
|
+
Throw = 24,
|
|
57
|
+
/// EnterTry: push handler (catch offset u16). Catch offset = bytes from end of this insn.
|
|
58
|
+
EnterTry = 25,
|
|
59
|
+
/// ExitTry: pop try handler
|
|
60
|
+
ExitTry = 26,
|
|
61
|
+
/// Concat arrays: pop right, pop left, push left.concat(right). For spread.
|
|
62
|
+
ConcatArray = 27,
|
|
63
|
+
/// Merge objects: pop right, pop left, push Object.assign({}, left, right). For object spread.
|
|
64
|
+
MergeObject = 28,
|
|
65
|
+
/// Call with spread: pop args array, pop callee, call callee(...args).
|
|
66
|
+
CallSpread = 29,
|
|
67
|
+
/// Get property optional: like GetMember but returns Null if obj is null or prop missing.
|
|
68
|
+
GetMemberOptional = 30,
|
|
69
|
+
/// Pop array, sort numerically in place (operand: u8 0=asc, 1=desc), push array.
|
|
70
|
+
/// Fast path for arr.sort((a,b)=>a-b) / arr.sort((a,b)=>b-a).
|
|
71
|
+
ArraySortNumeric = 31,
|
|
72
|
+
/// Pop array, sort by numeric property (operands: u16 prop_name_const_idx, u16 0=asc/1=desc).
|
|
73
|
+
/// Fast path for arr.sort((a,b)=>a.prop-b.prop).
|
|
74
|
+
ArraySortByProperty = 32,
|
|
75
|
+
/// arr.map(x => x) - identity, returns array clone.
|
|
76
|
+
ArrayMapIdentity = 33,
|
|
77
|
+
/// arr.map(x => x op const) or arr.map(x => const op x). Operands: u8 binop, u16 const_idx, u8 param_left (0=param on left e.g. x*2, 1=param on right e.g. 2*x).
|
|
78
|
+
ArrayMapBinOp = 34,
|
|
79
|
+
/// arr.filter(x => x op const) or arr.filter(x => const op x). Operands: u8 binop, u16 const_idx, u8 param_left. Keeps elements where result is truthy.
|
|
80
|
+
ArrayFilterBinOp = 35,
|
|
81
|
+
/// Load built-in module export. Operands: u16 spec_const_idx, u16 export_name_const_idx. Pushes Value.
|
|
82
|
+
LoadNativeExport = 36,
|
|
83
|
+
/// `new callee(...args)` (operand: u16 arg count). Stack: callee, then args — same as Call.
|
|
84
|
+
Construct = 37,
|
|
85
|
+
/// `new callee(...spread)` — stack: args array, then callee (same order as CallSpread).
|
|
86
|
+
ConstructSpread = 38,
|
|
87
|
+
/// Declare `let`/`const` in the current lexical frame (operand: u16 name index). Pops value.
|
|
88
|
+
/// Does not walk enclosing scopes or globals (unlike [`StoreVar`]).
|
|
89
|
+
DeclareVar = 39,
|
|
90
|
+
/// Enter a block scope; pairs with [`ExitBlock`].
|
|
91
|
+
EnterBlock = 40,
|
|
92
|
+
/// Exit innermost block scope and restore shadowed bindings.
|
|
93
|
+
ExitBlock = 41,
|
|
94
|
+
/// Like [`DeclareVar`] but does not record block-scope undo (for `for`/`for-of` header bindings).
|
|
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,
|
|
99
|
+
/// Load a local variable by frame slot (operand: u16 slot). Fast path: direct
|
|
100
|
+
/// index into the current call frame's locals, no name lookup.
|
|
101
|
+
LoadLocal = 44,
|
|
102
|
+
/// Store top of stack into a local frame slot (operand: u16 slot). Leaves nothing.
|
|
103
|
+
StoreLocal = 45,
|
|
104
|
+
/// Load a captured variable from an enclosing frame (operands: u16 hops, u16 slot).
|
|
105
|
+
/// Walks `hops` parent frames, then indexes `slot`.
|
|
106
|
+
LoadUpvalue = 46,
|
|
107
|
+
/// Store top of stack into an enclosing frame slot (operands: u16 hops, u16 slot).
|
|
108
|
+
StoreUpvalue = 47,
|
|
109
|
+
/// Begin a per-iteration binding region for a loop variable (operand: u16 name index).
|
|
110
|
+
/// Registers the name so closures created in the loop body snapshot it into a fresh overlay
|
|
111
|
+
/// (ES `let` per-iteration semantics); the rest of the frame stays shared. Emitted only when
|
|
112
|
+
/// the loop body creates a closure, so closure-free (hot) loops are untouched.
|
|
113
|
+
LoopVarsBegin = 48,
|
|
114
|
+
/// End the innermost per-iteration binding region (no operand).
|
|
115
|
+
LoopVarsEnd = 49,
|
|
116
|
+
/// Push `Bool(param_index >= argc)` — true when the positional argument at `param_index`
|
|
117
|
+
/// was not supplied by the caller (operand: u16 param index). Emitted by the function
|
|
118
|
+
/// prologue so default parameter values apply only for *missing* args, matching the
|
|
119
|
+
/// interpreter: an explicit `null` argument does NOT trigger the default.
|
|
120
|
+
ArgMissing = 50,
|
|
121
|
+
/// Direct self-recursive call (operand: u16 arg count). Emitted by the compiler ONLY when a
|
|
122
|
+
/// `fn NAME` body calls `NAME(args)` and `NAME` is provably the function itself (not shadowed
|
|
123
|
+
/// by a param/local, not reassigned anywhere in the body). Args are on the stack as for `Call`,
|
|
124
|
+
/// but the callee is implicitly the currently-executing chunk — no name lookup, no closure
|
|
125
|
+
/// dispatch. The numeric JIT lowers this to a native recursive call (the big recursion win);
|
|
126
|
+
/// the VM runs the current chunk directly. Behaviour is identical to `LoadVar NAME; Call argc`.
|
|
127
|
+
SelfCall = 51,
|
|
128
|
+
/// Normalize the top-of-stack iterable for `for…of`: a JS iterator object (one with a
|
|
129
|
+
/// callable `next()` returning `{ value, done }`, e.g. a `Map`/`Set` `.values()` result)
|
|
130
|
+
/// is drained into an array; arrays/strings/anything else pass through unchanged. Emitted
|
|
131
|
+
/// right after the iterable expression so the existing index-based loop can iterate it.
|
|
132
|
+
IterNormalize = 52,
|
|
133
|
+
/// `delete obj[key]` / `delete obj.prop`. Pops `[obj, key]`, removes the property
|
|
134
|
+
/// (objects: drop the key; arrays: set the index to a null hole), pushes `true`.
|
|
135
|
+
DeleteIndex = 53,
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
impl Opcode {
|
|
139
|
+
/// Decode byte to opcode. Safe for b in 0..=53 (matches #[repr(u8)] discriminants).
|
|
140
|
+
#[inline]
|
|
141
|
+
pub fn from_u8(b: u8) -> Option<Opcode> {
|
|
142
|
+
if b <= 53 {
|
|
143
|
+
Some(unsafe { std::mem::transmute::<u8, Opcode>(b) })
|
|
144
|
+
} else {
|
|
145
|
+
None
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/// Size in bytes of this instruction at `ip` (including operands). Returns None if truncated.
|
|
150
|
+
pub fn instruction_size(self, code: &[u8], ip: usize) -> Option<usize> {
|
|
151
|
+
let size = match self {
|
|
152
|
+
Opcode::Nop
|
|
153
|
+
| Opcode::Pop
|
|
154
|
+
| Opcode::Dup
|
|
155
|
+
| Opcode::Return
|
|
156
|
+
| Opcode::ExitTry
|
|
157
|
+
| Opcode::ConcatArray
|
|
158
|
+
| Opcode::MergeObject
|
|
159
|
+
| Opcode::GetIndex
|
|
160
|
+
| Opcode::SetIndex
|
|
161
|
+
| Opcode::Throw
|
|
162
|
+
| Opcode::ArrayMapIdentity
|
|
163
|
+
| Opcode::CallSpread
|
|
164
|
+
| Opcode::ConstructSpread
|
|
165
|
+
| Opcode::EnterBlock
|
|
166
|
+
| Opcode::ExitBlock
|
|
167
|
+
| Opcode::LoopVarsEnd
|
|
168
|
+
| Opcode::IterNormalize
|
|
169
|
+
| Opcode::DeleteIndex
|
|
170
|
+
| Opcode::AwaitPromise => 1,
|
|
171
|
+
Opcode::ArraySortByProperty
|
|
172
|
+
| Opcode::ArrayMapBinOp
|
|
173
|
+
| Opcode::ArrayFilterBinOp
|
|
174
|
+
| Opcode::LoadNativeExport
|
|
175
|
+
| Opcode::LoadUpvalue
|
|
176
|
+
| Opcode::StoreUpvalue => 5,
|
|
177
|
+
// LoadLocal / StoreLocal take a single u16 operand → 3 bytes (default).
|
|
178
|
+
_ => 3,
|
|
179
|
+
};
|
|
180
|
+
if ip + size > code.len() {
|
|
181
|
+
return None;
|
|
182
|
+
}
|
|
183
|
+
Some(size)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
//! Peephole optimizations on bytecode (post-emission).
|
|
2
|
+
//! B2 from optimization plan: jump chaining, etc.
|
|
3
|
+
|
|
4
|
+
use std::collections::BTreeSet;
|
|
5
|
+
|
|
6
|
+
use crate::opcode::Opcode;
|
|
7
|
+
use crate::Chunk;
|
|
8
|
+
|
|
9
|
+
fn read_u16(code: &[u8], pos: usize) -> u16 {
|
|
10
|
+
if pos + 1 >= code.len() {
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
let a = code[pos] as u16;
|
|
14
|
+
let b = code[pos + 1] as u16;
|
|
15
|
+
(a << 8) | b
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
fn read_i16(code: &[u8], pos: usize) -> i16 {
|
|
19
|
+
read_u16(code, pos) as i16
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
fn write_u16(code: &mut [u8], pos: usize, v: u16) {
|
|
23
|
+
if pos + 1 < code.len() {
|
|
24
|
+
let bytes = v.to_be_bytes();
|
|
25
|
+
code[pos] = bytes[0];
|
|
26
|
+
code[pos + 1] = bytes[1];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// Size of instruction at `ip` in bytes. Returns None if invalid/truncated.
|
|
31
|
+
fn instruction_size(code: &[u8], ip: usize) -> Option<usize> {
|
|
32
|
+
if ip >= code.len() {
|
|
33
|
+
return None;
|
|
34
|
+
}
|
|
35
|
+
let opcode = Opcode::from_u8(code[ip])?;
|
|
36
|
+
opcode.instruction_size(code, ip)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Advance past `Nop` bytes left by other peepholes (`Dup`+`Pop` → `Nop`+`Nop`, etc.).
|
|
40
|
+
/// Jump resolution must not treat a `Nop` run as the end of a chain, or we leave a jump
|
|
41
|
+
/// targeting the middle of padding while `chain_jumps` redirects another jump past it —
|
|
42
|
+
/// that misaligns `||` short-circuit when nested in an outer `if`.
|
|
43
|
+
fn skip_leading_nops(code: &[u8], mut ip: usize) -> usize {
|
|
44
|
+
while ip < code.len() && Opcode::from_u8(code[ip]) == Some(Opcode::Nop) {
|
|
45
|
+
ip += 1;
|
|
46
|
+
}
|
|
47
|
+
ip
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// After a branch lands at `ip`, follow only **unconditional** `Jump` instructions.
|
|
51
|
+
/// Must not follow `JumpIfFalse`: that opcode is conditional; treating it like `Jump`
|
|
52
|
+
/// breaks short-circuit codegen (e.g. `a === 1 || b === 2` inside `if (...)`).
|
|
53
|
+
fn skip_unconditional_jump_chain(code: &[u8], mut ip: usize) -> Option<usize> {
|
|
54
|
+
ip = skip_leading_nops(code, ip);
|
|
55
|
+
let mut visited = 0u32;
|
|
56
|
+
const MAX_CHAIN: u32 = 1000;
|
|
57
|
+
loop {
|
|
58
|
+
if visited > MAX_CHAIN {
|
|
59
|
+
return None;
|
|
60
|
+
}
|
|
61
|
+
visited += 1;
|
|
62
|
+
if ip > code.len() {
|
|
63
|
+
return None;
|
|
64
|
+
}
|
|
65
|
+
if ip == code.len() {
|
|
66
|
+
return Some(ip);
|
|
67
|
+
}
|
|
68
|
+
let _ = instruction_size(code, ip)?;
|
|
69
|
+
let op = Opcode::from_u8(code[ip])?;
|
|
70
|
+
if op != Opcode::Jump {
|
|
71
|
+
return Some(ip);
|
|
72
|
+
}
|
|
73
|
+
let offset = read_i16(code, ip + 1) as isize;
|
|
74
|
+
ip = (ip as isize + 3 + offset).max(0) as usize;
|
|
75
|
+
ip = skip_leading_nops(code, ip);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// For a `Jump` or `JumpIfFalse` at `jump_ip`, return the final IP after resolving the
|
|
80
|
+
/// taken branch and then skipping through any **unconditional** `Jump` chain only.
|
|
81
|
+
fn final_jump_target(code: &[u8], jump_ip: usize) -> Option<usize> {
|
|
82
|
+
let _ = instruction_size(code, jump_ip)?;
|
|
83
|
+
let op = Opcode::from_u8(code[jump_ip])?;
|
|
84
|
+
let first_target = match op {
|
|
85
|
+
Opcode::Jump | Opcode::JumpIfFalse => {
|
|
86
|
+
let offset = read_i16(code, jump_ip + 1) as isize;
|
|
87
|
+
(jump_ip as isize + 3 + offset).max(0) as usize
|
|
88
|
+
}
|
|
89
|
+
_ => return Some(jump_ip),
|
|
90
|
+
};
|
|
91
|
+
let first_target = skip_leading_nops(code, first_target);
|
|
92
|
+
skip_unconditional_jump_chain(code, first_target)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/// Instruction boundaries from a linear scan (aligned bytecode from the compiler).
|
|
96
|
+
fn collect_insn_starts(code: &[u8]) -> BTreeSet<usize> {
|
|
97
|
+
let mut out = BTreeSet::new();
|
|
98
|
+
let mut ip = 0usize;
|
|
99
|
+
while ip < code.len() {
|
|
100
|
+
out.insert(ip);
|
|
101
|
+
let sz = instruction_size(code, ip).unwrap_or(1);
|
|
102
|
+
ip += sz;
|
|
103
|
+
}
|
|
104
|
+
out
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/// Replace instruction at [ip..ip+len) with Nops (preserves length, no offset updates).
|
|
108
|
+
fn nop_out(code: &mut [u8], ip: usize, len: usize) {
|
|
109
|
+
for i in 0..len {
|
|
110
|
+
if ip + i < code.len() {
|
|
111
|
+
code[ip + i] = Opcode::Nop as u8;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/// Remove redundant Dup + Pop (dup top then discard = no-op).
|
|
117
|
+
fn remove_dup_pop(code: &mut [u8]) {
|
|
118
|
+
let mut ip = 0;
|
|
119
|
+
while ip + 2 <= code.len() {
|
|
120
|
+
if Opcode::from_u8(code[ip]) == Some(Opcode::Dup)
|
|
121
|
+
&& Opcode::from_u8(code[ip + 1]) == Some(Opcode::Pop)
|
|
122
|
+
{
|
|
123
|
+
nop_out(code, ip, 2);
|
|
124
|
+
}
|
|
125
|
+
ip += instruction_size(code, ip).unwrap_or(1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/// Replace no-op jumps (Jump with offset 0) with Nops.
|
|
130
|
+
fn remove_noop_jumps(code: &mut [u8]) {
|
|
131
|
+
let mut ip = 0;
|
|
132
|
+
while ip < code.len() {
|
|
133
|
+
if Opcode::from_u8(code[ip]) == Some(Opcode::Jump) {
|
|
134
|
+
let offset = read_u16(code, ip + 1);
|
|
135
|
+
if offset == 0 {
|
|
136
|
+
nop_out(code, ip, 3);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
ip += instruction_size(code, ip).unwrap_or(1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// Apply jump chaining: if Jump/JumpIfFalse targets another jump, update to
|
|
144
|
+
/// jump directly to the final target.
|
|
145
|
+
fn chain_jumps(code: &mut [u8]) {
|
|
146
|
+
let insn_starts = collect_insn_starts(code);
|
|
147
|
+
let mut ip = 0;
|
|
148
|
+
while ip < code.len() {
|
|
149
|
+
let op = match Opcode::from_u8(code[ip]) {
|
|
150
|
+
Some(o) => o,
|
|
151
|
+
None => {
|
|
152
|
+
ip += 1;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
let size = match instruction_size(code, ip) {
|
|
157
|
+
Some(s) => s,
|
|
158
|
+
None => break,
|
|
159
|
+
};
|
|
160
|
+
match op {
|
|
161
|
+
Opcode::Jump | Opcode::JumpIfFalse => {
|
|
162
|
+
let current_offset = read_i16(code, ip + 1) as isize;
|
|
163
|
+
let current_target = (ip as isize + 3 + current_offset).max(0) as usize;
|
|
164
|
+
if let Some(final_target) = final_jump_target(code, ip) {
|
|
165
|
+
let target_ok =
|
|
166
|
+
final_target == code.len() || insn_starts.contains(&final_target);
|
|
167
|
+
if final_target != current_target && target_ok {
|
|
168
|
+
let new_offset = final_target as i32 - (ip + 3) as i32;
|
|
169
|
+
if (i16::MIN as i32..=i16::MAX as i32).contains(&new_offset) {
|
|
170
|
+
write_u16(code, ip + 1, (new_offset as i16) as u16);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
_ => {}
|
|
176
|
+
}
|
|
177
|
+
ip += size;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/// Run peephole optimizations on a chunk (and nested chunks).
|
|
182
|
+
pub fn optimize(chunk: &mut Chunk) {
|
|
183
|
+
remove_dup_pop(&mut chunk.code);
|
|
184
|
+
remove_noop_jumps(&mut chunk.code);
|
|
185
|
+
chain_jumps(&mut chunk.code);
|
|
186
|
+
for nested in &mut chunk.nested {
|
|
187
|
+
optimize(nested);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
//! Chunk serialization for embedding in native/WASM outputs.
|
|
2
|
+
//! Format: code, constants, names, nested (recursive).
|
|
3
|
+
|
|
4
|
+
use std::sync::Arc;
|
|
5
|
+
|
|
6
|
+
use super::{Chunk, Constant};
|
|
7
|
+
|
|
8
|
+
/// Serialize a chunk to bytes (includes nested chunks for functions).
|
|
9
|
+
pub fn serialize(chunk: &Chunk) -> Vec<u8> {
|
|
10
|
+
let mut out = Vec::new();
|
|
11
|
+
out.extend_from_slice(&(chunk.code.len() as u64).to_le_bytes());
|
|
12
|
+
out.extend_from_slice(&chunk.code);
|
|
13
|
+
out.extend_from_slice(&(chunk.constants.len() as u64).to_le_bytes());
|
|
14
|
+
for c in &chunk.constants {
|
|
15
|
+
match c {
|
|
16
|
+
Constant::Number(n) => {
|
|
17
|
+
out.push(0);
|
|
18
|
+
out.extend_from_slice(&n.to_le_bytes());
|
|
19
|
+
}
|
|
20
|
+
Constant::String(s) => {
|
|
21
|
+
out.push(1);
|
|
22
|
+
let b = s.as_bytes();
|
|
23
|
+
out.extend_from_slice(&(b.len() as u64).to_le_bytes());
|
|
24
|
+
out.extend_from_slice(b);
|
|
25
|
+
}
|
|
26
|
+
Constant::Bool(b) => {
|
|
27
|
+
out.push(2);
|
|
28
|
+
out.push(if *b { 1 } else { 0 });
|
|
29
|
+
}
|
|
30
|
+
Constant::Null => out.push(3),
|
|
31
|
+
Constant::Closure(idx) => {
|
|
32
|
+
out.push(4);
|
|
33
|
+
out.extend_from_slice(&(*idx as u64).to_le_bytes());
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
out.extend_from_slice(&(chunk.names.len() as u64).to_le_bytes());
|
|
38
|
+
for n in &chunk.names {
|
|
39
|
+
let b = n.as_bytes();
|
|
40
|
+
out.extend_from_slice(&(b.len() as u64).to_le_bytes());
|
|
41
|
+
out.extend_from_slice(b);
|
|
42
|
+
}
|
|
43
|
+
out.extend_from_slice(&(chunk.nested.len() as u64).to_le_bytes());
|
|
44
|
+
for nested in &chunk.nested {
|
|
45
|
+
let nested_bytes = serialize(nested);
|
|
46
|
+
out.extend_from_slice(&(nested_bytes.len() as u64).to_le_bytes());
|
|
47
|
+
out.extend_from_slice(&nested_bytes);
|
|
48
|
+
}
|
|
49
|
+
out.extend_from_slice(&chunk.rest_param_index.to_le_bytes());
|
|
50
|
+
out.extend_from_slice(&chunk.param_count.to_le_bytes());
|
|
51
|
+
out.extend_from_slice(&chunk.num_slots.to_le_bytes());
|
|
52
|
+
out.push(if chunk.slot_based { 1 } else { 0 });
|
|
53
|
+
out
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// Deserialize a chunk from bytes.
|
|
57
|
+
pub fn deserialize(mut data: &[u8]) -> Result<Chunk, String> {
|
|
58
|
+
let read_u64 = |d: &mut &[u8]| {
|
|
59
|
+
if d.len() < 8 {
|
|
60
|
+
return Err("Unexpected EOF".to_string());
|
|
61
|
+
}
|
|
62
|
+
let (head, tail) = d.split_at(8);
|
|
63
|
+
*d = tail;
|
|
64
|
+
Ok(u64::from_le_bytes(head.try_into().unwrap()))
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
let code_len = read_u64(&mut data)? as usize;
|
|
68
|
+
if data.len() < code_len {
|
|
69
|
+
return Err("Truncated code".to_string());
|
|
70
|
+
}
|
|
71
|
+
let (code_bytes, rest) = data.split_at(code_len);
|
|
72
|
+
data = rest;
|
|
73
|
+
let code = code_bytes.to_vec();
|
|
74
|
+
|
|
75
|
+
let const_count = read_u64(&mut data)? as usize;
|
|
76
|
+
let mut constants = Vec::with_capacity(const_count);
|
|
77
|
+
for _ in 0..const_count {
|
|
78
|
+
if data.is_empty() {
|
|
79
|
+
return Err("Truncated constant".to_string());
|
|
80
|
+
}
|
|
81
|
+
let tag = data[0];
|
|
82
|
+
data = &data[1..];
|
|
83
|
+
let c = match tag {
|
|
84
|
+
0 => {
|
|
85
|
+
if data.len() < 8 {
|
|
86
|
+
return Err("Truncated number".to_string());
|
|
87
|
+
}
|
|
88
|
+
let (n_bytes, rest) = data.split_at(8);
|
|
89
|
+
data = rest;
|
|
90
|
+
Constant::Number(f64::from_le_bytes(n_bytes.try_into().unwrap()))
|
|
91
|
+
}
|
|
92
|
+
1 => {
|
|
93
|
+
let str_len = read_u64(&mut data)? as usize;
|
|
94
|
+
if data.len() < str_len {
|
|
95
|
+
return Err("Truncated string".to_string());
|
|
96
|
+
}
|
|
97
|
+
let (s_bytes, rest) = data.split_at(str_len);
|
|
98
|
+
data = rest;
|
|
99
|
+
Constant::String(Arc::from(String::from_utf8_lossy(s_bytes).into_owned()))
|
|
100
|
+
}
|
|
101
|
+
2 => {
|
|
102
|
+
if data.is_empty() {
|
|
103
|
+
return Err("Truncated bool".to_string());
|
|
104
|
+
}
|
|
105
|
+
let b = data[0] != 0;
|
|
106
|
+
data = &data[1..];
|
|
107
|
+
Constant::Bool(b)
|
|
108
|
+
}
|
|
109
|
+
3 => Constant::Null,
|
|
110
|
+
4 => {
|
|
111
|
+
let idx = read_u64(&mut data)? as usize;
|
|
112
|
+
Constant::Closure(idx)
|
|
113
|
+
}
|
|
114
|
+
_ => return Err(format!("Unknown constant tag: {}", tag)),
|
|
115
|
+
};
|
|
116
|
+
constants.push(c);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let names_count = read_u64(&mut data)? as usize;
|
|
120
|
+
let mut names = Vec::with_capacity(names_count);
|
|
121
|
+
for _ in 0..names_count {
|
|
122
|
+
let n_len = read_u64(&mut data)? as usize;
|
|
123
|
+
if data.len() < n_len {
|
|
124
|
+
return Err("Truncated name".to_string());
|
|
125
|
+
}
|
|
126
|
+
let (n_bytes, rest) = data.split_at(n_len);
|
|
127
|
+
data = rest;
|
|
128
|
+
names.push(Arc::from(String::from_utf8_lossy(n_bytes).into_owned()));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let nested_count = read_u64(&mut data)? as usize;
|
|
132
|
+
let mut nested = Vec::with_capacity(nested_count);
|
|
133
|
+
for _ in 0..nested_count {
|
|
134
|
+
let nested_len = read_u64(&mut data)? as usize;
|
|
135
|
+
if data.len() < nested_len {
|
|
136
|
+
return Err("Truncated nested chunk".to_string());
|
|
137
|
+
}
|
|
138
|
+
let (nested_data, rest) = data.split_at(nested_len);
|
|
139
|
+
data = rest;
|
|
140
|
+
nested.push(deserialize(nested_data)?);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let rest_param_index = if data.len() >= 2 {
|
|
144
|
+
let (r_bytes, rest) = data.split_at(2);
|
|
145
|
+
data = rest;
|
|
146
|
+
u16::from_le_bytes(r_bytes.try_into().unwrap())
|
|
147
|
+
} else {
|
|
148
|
+
super::NO_REST_PARAM
|
|
149
|
+
};
|
|
150
|
+
let param_count = if data.len() >= 2 {
|
|
151
|
+
let (p_bytes, rest) = data.split_at(2);
|
|
152
|
+
data = rest;
|
|
153
|
+
u16::from_le_bytes(p_bytes.try_into().unwrap())
|
|
154
|
+
} else {
|
|
155
|
+
0
|
|
156
|
+
};
|
|
157
|
+
let num_slots = if data.len() >= 2 {
|
|
158
|
+
let (s_bytes, rest) = data.split_at(2);
|
|
159
|
+
data = rest;
|
|
160
|
+
u16::from_le_bytes(s_bytes.try_into().unwrap())
|
|
161
|
+
} else {
|
|
162
|
+
0
|
|
163
|
+
};
|
|
164
|
+
let slot_based = if !data.is_empty() {
|
|
165
|
+
let b = data[0] != 0;
|
|
166
|
+
data = &data[1..];
|
|
167
|
+
b
|
|
168
|
+
} else {
|
|
169
|
+
false
|
|
170
|
+
};
|
|
171
|
+
let _ = data;
|
|
172
|
+
|
|
173
|
+
// Inline caches are a runtime-only cache, not serialized — start empty, sized to `names`.
|
|
174
|
+
let inline_caches = crate::chunk::InlineCaches(
|
|
175
|
+
(0..names.len())
|
|
176
|
+
.map(|_| std::sync::atomic::AtomicU64::new(0))
|
|
177
|
+
.collect(),
|
|
178
|
+
);
|
|
179
|
+
Ok(Chunk {
|
|
180
|
+
code,
|
|
181
|
+
constants,
|
|
182
|
+
names,
|
|
183
|
+
nested,
|
|
184
|
+
rest_param_index,
|
|
185
|
+
param_count,
|
|
186
|
+
num_slots,
|
|
187
|
+
slot_based,
|
|
188
|
+
inline_caches,
|
|
189
|
+
// Debug-only; not part of the serialized format (issue #74).
|
|
190
|
+
lines: Vec::new(),
|
|
191
|
+
source: None,
|
|
192
|
+
})
|
|
193
|
+
}
|