use crate::runner::ds::value::JsValue;
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(u8)]
pub enum RegOpCode {
LoadConst,
LoadUndefined,
LoadNull,
LoadTrue,
LoadFalse,
Move,
Add,
Sub,
Mul,
Div,
Mod,
Negate,
BitAnd,
BitOr,
BitXor,
BitNot,
ShiftLeft,
ShiftRight,
UShiftRight,
StrictEqual,
StrictNotEqual,
Equal,
NotEqual,
LessThan,
LessEqual,
GreaterThan,
GreaterEqual,
Not,
TypeOf,
UnaryPlus,
GetVar,
SetVar,
DeclareVar,
DeclareLet,
DeclareConst,
InitVar,
InitBinding,
GetProp,
SetProp,
GetElem,
SetElem,
Jump,
JumpIfFalse,
JumpIfTrue,
Call,
CallMethod,
Return,
Halt,
}
#[derive(Debug, Clone)]
pub struct RegInstruction {
pub op: RegOpCode,
pub dst: u32,
pub src1: u32,
pub src2: u32,
pub imm: u32,
}
impl RegInstruction {
pub fn new(op: RegOpCode, dst: u32, src1: u32, src2: u32, imm: u32) -> Self {
RegInstruction { op, dst, src1, src2, imm }
}
pub fn simple(op: RegOpCode) -> Self {
RegInstruction::new(op, 0, 0, 0, 0)
}
pub fn with_dst(op: RegOpCode, dst: u32) -> Self {
RegInstruction::new(op, dst, 0, 0, 0)
}
pub fn with_dst_src(op: RegOpCode, dst: u32, src1: u32) -> Self {
RegInstruction::new(op, dst, src1, 0, 0)
}
pub fn with_dst_srcs(op: RegOpCode, dst: u32, src1: u32, src2: u32) -> Self {
RegInstruction::new(op, dst, src1, src2, 0)
}
pub fn with_dst_imm(op: RegOpCode, dst: u32, imm: u32) -> Self {
RegInstruction::new(op, dst, 0, 0, imm)
}
pub fn with_src_imm(op: RegOpCode, src1: u32, imm: u32) -> Self {
RegInstruction::new(op, 0, src1, 0, imm)
}
pub fn with_srcs_imm(op: RegOpCode, src1: u32, src2: u32, imm: u32) -> Self {
RegInstruction::new(op, 0, src1, src2, imm)
}
}
#[derive(Debug, Clone)]
pub struct RegChunk {
pub code: Vec<RegInstruction>,
pub constants: Vec<JsValue>,
pub names: Vec<String>,
pub locals: Vec<RegLocal>,
pub register_count: u32,
}
impl RegChunk {
pub fn new() -> Self {
RegChunk {
code: Vec::new(),
constants: Vec::new(),
names: Vec::new(),
locals: Vec::new(),
register_count: 0,
}
}
pub fn emit(&mut self, instr: RegInstruction) -> usize {
let idx = self.code.len();
self.code.push(instr);
idx
}
pub fn emit_op(&mut self, op: RegOpCode) -> usize {
self.emit(RegInstruction::simple(op))
}
pub fn add_constant(&mut self, value: JsValue) -> u32 {
let idx = self.constants.len();
self.constants.push(value);
idx as u32
}
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
}
#[inline]
pub fn get_name(&self, idx: u32) -> &str {
&self.names[idx as usize]
}
pub fn add_local(&mut self, name_idx: u32, reg: u32) -> u32 {
let idx = self.locals.len();
self.locals.push(RegLocal { name_idx, reg });
idx as u32
}
#[inline]
pub fn get_local_name(&self, local_idx: u32) -> &str {
let name_idx = self.locals[local_idx as usize].name_idx;
self.get_name(name_idx)
}
pub fn set_register_count(&mut self, count: u32) {
self.register_count = count;
}
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} {:?} dst={} src1={} src2={} imm={}", i, instr.op, instr.dst, instr.src1, instr.src2, instr.imm));
match instr.op {
RegOpCode::LoadConst => {
if let Some(val) = self.constants.get(instr.imm as usize) {
out.push_str(&format!(" const={}", val));
}
}
RegOpCode::GetVar | RegOpCode::SetVar
| RegOpCode::DeclareVar | RegOpCode::DeclareLet
| RegOpCode::DeclareConst | RegOpCode::InitVar
| RegOpCode::InitBinding | RegOpCode::GetProp
| RegOpCode::SetProp => {
if let Some(name) = self.names.get(instr.imm as usize) {
out.push_str(&format!(" name=\"{}\"", name));
}
}
RegOpCode::CallMethod => {
if let Some(name) = self.names.get(instr.src2 as usize) {
out.push_str(&format!(" method=\"{}\"", name));
}
}
RegOpCode::Jump | RegOpCode::JumpIfFalse | RegOpCode::JumpIfTrue => {
out.push_str(&format!(" -> {:04}", instr.imm));
}
_ => {}
}
out.push('\n');
}
out
}
}
#[derive(Debug, Clone)]
pub struct RegLocal {
pub name_idx: u32,
pub reg: u32,
}