1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
//! Bytecode instruction set and chunk structure for the JIT compiler.
//!
//! Defines a flat, stack-based bytecode IR that the compiler emits
//! and the VM executes.
use crate::runner::ds::value::JsValue;
/// Bytecode opcodes for the stack-based VM.
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(u8)]
pub enum OpCode {
// ── Constants & Literals ──────────────────────────────────
/// Push a constant from the constant pool onto the stack.
Constant,
/// Push `undefined` onto the stack.
Undefined,
/// Push `null` onto the stack.
Null,
/// Push `true` onto the stack.
True,
/// Push `false` onto the stack.
False,
// ── Arithmetic ───────────────────────────────────────────
/// Pop two values, push their sum.
Add,
/// Pop two values, push their difference.
Sub,
/// Pop two values, push their product.
Mul,
/// Pop two values, push their quotient.
Div,
/// Pop two values, push their remainder.
Mod,
/// Pop one value, push its numeric negation.
Negate,
// ── Bitwise ──────────────────────────────────────────────
/// Bitwise AND.
BitAnd,
/// Bitwise OR.
BitOr,
/// Bitwise XOR.
BitXor,
/// Bitwise NOT.
BitNot,
/// Left shift.
ShiftLeft,
/// Signed right shift.
ShiftRight,
/// Unsigned right shift.
UShiftRight,
// ── Comparison ───────────────────────────────────────────
/// Strict equality (`===`).
StrictEqual,
/// Strict inequality (`!==`).
StrictNotEqual,
/// Abstract equality (`==`).
Equal,
/// Abstract inequality (`!=`).
NotEqual,
/// Less than.
LessThan,
/// Less than or equal.
LessEqual,
/// Greater than.
GreaterThan,
/// Greater than or equal.
GreaterEqual,
// ── Logical / Unary ──────────────────────────────────────
/// Logical NOT.
Not,
/// `typeof` operator — pops value, pushes type string.
TypeOf,
/// `void` operator — pops value, pushes undefined.
Void,
/// Unary `+` — converts to number.
UnaryPlus,
// ── Variables ────────────────────────────────────────────
/// Get a global/local variable by name (operand: constant pool index of name string).
GetVar,
/// Set a variable by name (operand: constant pool index of name string).
/// The value to assign is on top of the stack.
SetVar,
/// Declare a `var` binding (operand: constant pool index of name string).
DeclareVar,
/// Declare a `let` binding (operand: constant pool index of name string).
DeclareLet,
/// Declare a `const` binding (operand: constant pool index of name string).
DeclareConst,
/// Initialize a binding with the value on top of the stack.
InitVar,
/// Initialize a let/const binding with the value on top of the stack.
InitBinding,
/// Get a local slot by index (operand: local slot index).
GetLocal,
/// Set a local slot by index (operand: local slot index).
SetLocal,
/// Initialize a local slot (operand: local slot index).
InitLocal,
// ── Control Flow ─────────────────────────────────────────
/// Unconditional jump (operand: absolute offset).
Jump,
/// Jump if top of stack is falsy (operand: absolute offset). Pops the value.
JumpIfFalse,
/// Jump if top of stack is truthy (operand: absolute offset). Pops the value.
JumpIfTrue,
// ── Loops ────────────────────────────────────────────────
/// Marker for loop start (used by break/continue resolution).
LoopStart,
// ── Stack manipulation ───────────────────────────────────
/// Pop and discard the top of the stack.
Pop,
/// Duplicate the top of the stack.
Dup,
/// Duplicate the top two stack values (a,b -> a,b,a,b).
Dup2,
// ── Scope ────────────────────────────────────────────────
/// Push a new block scope.
PushScope,
/// Pop a block scope.
PopScope,
// ── Objects & Properties ─────────────────────────────────
/// Get a property: pop object and push object.property.
/// Operand: constant pool index of property name.
GetProp,
/// Set a property: stack has [value, object].
/// Operand: constant pool index of property name.
SetProp,
/// Get a computed property: stack has [key, object].
GetElem,
/// Set a computed property: stack has [value, key, object].
SetElem,
// ── Function calls ───────────────────────────────────────
/// Call a function. Operand: argument count.
/// Stack: [arg_n, ..., arg_1, callee]
Call,
/// Call a method. Operand: argument count.
/// Stack: [arg_n, ..., arg_1, object]
/// Second operand: constant pool index of method name.
CallMethod,
// ── Misc ─────────────────────────────────────────────────
/// Return from the current function/script. Pops return value from stack.
Return,
/// Halt execution (end of script).
Halt,
// ── Pre/Post increment/decrement ─────────────────────────
/// Pre-increment a variable (++x). Operand: constant pool index of name.
PreIncVar,
/// Pre-decrement a variable (--x). Operand: constant pool index of name.
PreDecVar,
/// Post-increment a variable (x++). Operand: constant pool index of name.
PostIncVar,
/// Post-decrement a variable (x--). Operand: constant pool index of name.
PostDecVar,
// ── Compound assignment helpers ──────────────────────────
/// Get a variable for compound update. Operand: constant pool index of name.
GetVarForUpdate,
// ── Closures ────────────────────────────────────────────
/// Create a closure from a function template.
/// Operand: index into the chunk's `functions` table.
/// Pushes the resulting function object onto the stack.
MakeClosure,
}
/// A single bytecode instruction with optional operands.
#[derive(Debug, Clone)]
pub struct Instruction {
pub op: OpCode,
/// Primary operand (constant pool index, jump offset, arg count, etc.).
pub operand: u32,
/// Secondary operand (used by CallMethod for method name index).
pub operand2: u32,
/// Tertiary operand (used by CallMethod for object name index).
pub operand3: u32,
}
impl Instruction {
pub fn simple(op: OpCode) -> Self {
Instruction { op, operand: 0, operand2: 0, operand3: 0 }
}
pub fn with_operand(op: OpCode, operand: u32) -> Self {
Instruction { op, operand, operand2: 0, operand3: 0 }
}
pub fn with_two_operands(op: OpCode, operand: u32, operand2: u32) -> Self {
Instruction { op, operand, operand2, operand3: 0 }
}
pub fn with_three_operands(op: OpCode, operand: u32, operand2: u32, operand3: u32) -> Self {
Instruction { op, operand, operand2, operand3 }
}
}
/// A stored function template — raw pointers to AST nodes captured at compile time.
#[derive(Debug, Clone, Copy)]
pub struct FunctionTemplate {
/// Pointer to the function body AST node.
pub body_ptr: *const crate::parser::ast::FunctionBodyData,
/// Pointer to the formal parameter list.
pub params_ptr: *const Vec<crate::parser::ast::PatternType>,
}
// Safety: FunctionTemplate is only used within a single thread.
unsafe impl Send for FunctionTemplate {}
unsafe impl Sync for FunctionTemplate {}
/// A compiled chunk of bytecode with its constant pool.
#[derive(Debug, Clone)]
pub struct Chunk {
/// The bytecode instructions.
pub code: Vec<Instruction>,
/// Constant pool — holds literal values.
pub constants: Vec<JsValue>,
/// Deduplicated name table for variable/property names.
/// Indexed by operand in GetVar/SetVar/etc. opcodes.
/// Separate from constants to allow zero-copy `&str` access in the VM.
pub names: Vec<String>,
/// Local slots table. Each entry stores an index into `names`.
/// Slot index is used by GetLocal/SetLocal/InitLocal opcodes.
pub locals: Vec<u32>,
/// Function templates for MakeClosure.
pub functions: Vec<FunctionTemplate>,
}
impl Chunk {
pub fn new() -> Self {
Chunk {
code: Vec::new(),
constants: Vec::new(),
names: Vec::new(),
locals: Vec::new(),
functions: Vec::new(),
}
}
/// Add a function template and return its index.
pub fn add_function(&mut self, template: FunctionTemplate) -> u32 {
let idx = self.functions.len();
self.functions.push(template);
idx as u32
}
/// Emit an instruction and return its index.
pub fn emit(&mut self, instr: Instruction) -> usize {
let idx = self.code.len();
self.code.push(instr);
idx
}
/// Emit a simple (no-operand) instruction.
pub fn emit_op(&mut self, op: OpCode) -> usize {
self.emit(Instruction::simple(op))
}
/// Emit an instruction with one operand.
pub fn emit_with(&mut self, op: OpCode, operand: u32) -> usize {
self.emit(Instruction::with_operand(op, operand))
}
/// Add a constant to the pool and return its index.
pub fn add_constant(&mut self, value: JsValue) -> u32 {
let idx = self.constants.len();
self.constants.push(value);
idx as u32
}
/// Add a name to the deduplicated name table and return its index.
/// Used for variable names, property names — allows zero-copy `&str` access in the VM.
pub fn add_name(&mut self, s: &str) -> u32 {
for (i, existing) in self.names.iter().enumerate() {
if existing == s {
return i as u32;
}
}
let idx = self.names.len();
self.names.push(s.to_string());
idx as u32
}
/// Add a local slot for a name index and return its slot index.
pub fn add_local(&mut self, name_idx: u32) -> u32 {
let slot = self.locals.len();
self.locals.push(name_idx);
slot as u32
}
/// Get a local slot's name (zero-copy reference).
#[inline]
pub fn get_local_name(&self, slot: u32) -> &str {
let name_idx = self.locals[slot as usize];
self.get_name(name_idx)
}
/// Get a name by index (zero-copy reference).
#[inline]
pub fn get_name(&self, idx: u32) -> &str {
&self.names[idx as usize]
}
/// Patch a jump instruction's operand to point to the current code position.
pub fn patch_jump(&mut self, jump_idx: usize) {
self.code[jump_idx].operand = self.code.len() as u32;
}
/// Get the current code position (for jump targets).
pub fn current_pos(&self) -> usize {
self.code.len()
}
/// Disassemble the chunk for debugging.
pub fn disassemble(&self, name: &str) -> String {
let mut out = format!("== {} ==\n", name);
for (i, instr) in self.code.iter().enumerate() {
out.push_str(&format!("{:04} {:?}", i, instr.op));
match instr.op {
OpCode::Constant => {
let val = &self.constants[instr.operand as usize];
out.push_str(&format!(" {} ({})", instr.operand, val));
}
OpCode::GetVar | OpCode::SetVar | OpCode::DeclareVar
| OpCode::DeclareLet | OpCode::DeclareConst
| OpCode::InitVar | OpCode::InitBinding
| OpCode::GetProp | OpCode::SetProp
| OpCode::PreIncVar | OpCode::PreDecVar
| OpCode::PostIncVar | OpCode::PostDecVar
| OpCode::GetVarForUpdate => {
let name_str = self.get_name(instr.operand);
out.push_str(&format!(" \"{}\"", name_str));
}
OpCode::GetLocal | OpCode::SetLocal | OpCode::InitLocal => {
let name_str = self.get_local_name(instr.operand);
out.push_str(&format!(" slot={} \"{}\"", instr.operand, name_str));
}
OpCode::Jump | OpCode::JumpIfFalse | OpCode::JumpIfTrue => {
out.push_str(&format!(" -> {:04}", instr.operand));
}
OpCode::Call => {
out.push_str(&format!(" argc={}", instr.operand));
}
OpCode::MakeClosure => {
out.push_str(&format!(" func_idx={}", instr.operand));
}
OpCode::CallMethod => {
let method = self.get_name(instr.operand2);
let obj_name = self.get_name(instr.operand3);
out.push_str(&format!(" argc={} method=\"{}\" obj=\"{}\"", instr.operand, method, obj_name));
}
_ => {}
}
out.push('\n');
}
out
}
}