158 lines
4.3 KiB
Rust
158 lines
4.3 KiB
Rust
//! 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<Instr>) -> 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<JsValue>,
|
|
out: &mut Vec<Instr>,
|
|
) -> 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
|
|
}
|