//! Process instrs that are a simple sequence of other instrs //! //! Useless instrs are replaced with `nop` since simply dropping instrs can break jump targets use super::{ENV_ARG, PROTO_FIELD}; use crate::{ libqjs::parse::{Atom, ImmOperand as Imm, Instr, JsValue, opcodes::OpCode}, native, }; #[allow(non_snake_case)] pub fn try_replace_expansion(instr: &Instr, out: &mut Vec) -> bool { use OpCode::*; let seq = match (instr.op, instr.imm) { (Call0, _) => [ (Nop, Imm::None), (Nop, Imm::None), (Undefined, Imm::None), (Call, Imm::Npop(1)), ], (Call1, _) => [ (Nop, Imm::None), (Nop, Imm::None), (Undefined, Imm::None), (Call, Imm::Npop(2)), ], (Call2, _) => [ (Nop, Imm::None), (Nop, Imm::None), (Undefined, Imm::None), (Call, Imm::Npop(3)), ], (Call3, _) => [ (Nop, Imm::None), (Nop, Imm::None), (Undefined, Imm::None), (Call, Imm::Npop(4)), ], (Call, Imm::Npop(n)) => [ (Nop, Imm::None), (Nop, Imm::None), (Undefined, Imm::None), (Call, Imm::Npop(n + 1)), ], (TailCall, Imm::Npop(n)) => [ (Nop, Imm::None), (Undefined, Imm::None), (Call, Imm::Npop(n + 1)), (Return, Imm::None), ], (TailCallMethod, imm) => [ (Nop, Imm::None), (Nop, Imm::None), (CallMethod, imm), (Return, Imm::None), ], (GetField2, imm) => [ (Nop, Imm::None), (Nop, Imm::None), (Dup, Imm::None), (GetField, imm), ], (GetLoc0Loc1, _) => [ (Nop, Imm::None), (Nop, Imm::None), (GetLoc0, Imm::None), (GetLoc1, Imm::None), ], (IncLoc, imm) => [ (Nop, Imm::None), (GetLoc, imm), (Inc, Imm::None), (PutLoc, imm), ], (DecLoc, imm) => [ (Nop, Imm::None), (GetLoc, imm), (Dec, Imm::None), (PutLoc, imm), ], // The added value is on the stack, the local is the left operand (AddLoc, imm) => [ (GetLoc, imm), (Swap, Imm::None), (Add, Imm::None), (PutLoc, imm), ], (SetProto, _) => [ (Nop, Imm::None), (Nop, Imm::None), (Nop, Imm::None), (DefineField, Imm::Atom(PROTO_FIELD)), ], (DefineMethod, Imm::AtomU8 { atom, arg }) => { assert!(arg & 3 == 0, "getters and setters are not supported"); [ (Nop, Imm::None), (Nop, Imm::None), (Nop, Imm::None), (DefineField, Imm::Atom(atom)), ] } // The fn itself, which is its env (SpecialObject, Imm::U8(2)) => [ (Nop, Imm::None), (Nop, Imm::None), (Nop, Imm::None), (GetArg, Imm::Arg(ENV_ARG)), ], // [[HomeObject]], which only `super` reads. `super` is not supported (SpecialObject, Imm::U8(4)) => unimplemented!(), // A class constructor called without `new` throws. That is not checked for the moment (CheckCtor, _) => [ (Nop, Imm::None), (Nop, Imm::None), (Nop, Imm::None), (Nop, Imm::None), ], // arguments, new.target, import.meta (SpecialObject, _) => unimplemented!(), _ => return false, }; for (op, imm) in seq { out.push(Instr { pc: instr.pc, op, imm, }); } true } /// Replace a read of a global with the constant it is (see `native`) pub fn replace_get_var( instr: &Instr, atoms: &[Atom], cpool: &mut Vec, out: &mut Vec, ) -> bool { if instr.op != OpCode::GetVar { return false; } cpool.push(native::global(instr.imm.atom(), atoms)); out.push(Instr { pc: instr.pc, op: OpCode::PushConst, imm: Imm::Const(cpool.len() as u32 - 1), }); true }