mirror of https://github.com/boa-dev/boa.git
Browse Source
- Implemented Jump and JumpIfFalse opcodes - Implemented If in vm - Implement assign to identifiers - Implement JumpIfTrue opcode - Implemented short circuit operators - Implement while loop - Implement do-while loop - Pop expressions - Split compilation for epression and statement - simplify boolean compilation - Implement loop break and continue with labels - Implement getting property from object - Implement setting object properties - Implement binary assign - Implement throw - Split variable names from literals - Implement conditional operator - Implement array expressions without spread - Implement some unary operators - Add accessor types - Implement this - Add opcode for pushing rationals - Implement instanceof operatorpull/1387/head
Halid Odat
3 years ago
committed by
GitHub
25 changed files with 2046 additions and 720 deletions
@ -0,0 +1,693 @@ |
|||||||
|
use crate::{ |
||||||
|
syntax::ast::{ |
||||||
|
node::{GetConstField, GetField, Identifier, StatementList}, |
||||||
|
op::{AssignOp, BinOp, BitOp, CompOp, LogOp, NumOp, UnaryOp}, |
||||||
|
Const, Node, |
||||||
|
}, |
||||||
|
value::{RcBigInt, RcString}, |
||||||
|
vm::{CodeBlock, Opcode}, |
||||||
|
Value, |
||||||
|
}; |
||||||
|
|
||||||
|
use std::collections::HashMap; |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn u16_to_array(value: u16) -> [u8; 2] { |
||||||
|
// Safety: Transmuting a `u16` primitive to
|
||||||
|
// an array of 2 bytes is safe.
|
||||||
|
unsafe { std::mem::transmute(value) } |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn u32_to_array(value: u32) -> [u8; 4] { |
||||||
|
// Safety: Transmuting a `u32` primitive to
|
||||||
|
// an array of 4 bytes is safe.
|
||||||
|
unsafe { std::mem::transmute(value) } |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn u64_to_array(value: u64) -> [u8; 8] { |
||||||
|
// Safety: Transmuting a `u64` primitive to
|
||||||
|
// an array of 8 bytes is safe.
|
||||||
|
unsafe { std::mem::transmute(value) } |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)] |
||||||
|
enum Literal { |
||||||
|
String(RcString), |
||||||
|
BigInt(RcBigInt), |
||||||
|
} |
||||||
|
|
||||||
|
#[must_use] |
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct Label { |
||||||
|
index: u32, |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug, Clone)] |
||||||
|
struct LoopControlInfo { |
||||||
|
label: Option<Box<str>>, |
||||||
|
loop_start: u32, |
||||||
|
continues: Vec<Label>, |
||||||
|
breaks: Vec<Label>, |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
enum Access<'a> { |
||||||
|
Variable { name: &'a Identifier }, |
||||||
|
ByName { node: &'a GetConstField }, |
||||||
|
ByValue { node: &'a GetField }, |
||||||
|
This, |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
pub struct ByteCompiler { |
||||||
|
code_block: CodeBlock, |
||||||
|
literals_map: HashMap<Literal, u32>, |
||||||
|
names_map: HashMap<RcString, u32>, |
||||||
|
loops: Vec<LoopControlInfo>, |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for ByteCompiler { |
||||||
|
fn default() -> Self { |
||||||
|
Self::new() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl ByteCompiler { |
||||||
|
/// Represents a placeholder address that will be patched later.
|
||||||
|
const DUMMY_ADDRESS: u32 = u32::MAX; |
||||||
|
|
||||||
|
#[inline] |
||||||
|
pub fn new() -> Self { |
||||||
|
Self { |
||||||
|
code_block: CodeBlock::new(), |
||||||
|
literals_map: HashMap::new(), |
||||||
|
names_map: HashMap::new(), |
||||||
|
loops: Vec::new(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn get_or_insert_literal(&mut self, liternal: Literal) -> u32 { |
||||||
|
if let Some(index) = self.literals_map.get(&liternal) { |
||||||
|
return *index; |
||||||
|
} |
||||||
|
|
||||||
|
let value = match liternal.clone() { |
||||||
|
Literal::String(value) => Value::from(value), |
||||||
|
Literal::BigInt(value) => Value::from(value), |
||||||
|
}; |
||||||
|
|
||||||
|
let index = self.code_block.literals.len() as u32; |
||||||
|
self.code_block.literals.push(value); |
||||||
|
self.literals_map.insert(liternal, index); |
||||||
|
index |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn get_or_insert_name(&mut self, name: &str) -> u32 { |
||||||
|
if let Some(index) = self.names_map.get(name) { |
||||||
|
return *index; |
||||||
|
} |
||||||
|
|
||||||
|
let name: RcString = name.into(); |
||||||
|
let index = self.code_block.names.len() as u32; |
||||||
|
self.code_block.names.push(name.clone()); |
||||||
|
self.names_map.insert(name, index); |
||||||
|
index |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn next_opcode_location(&mut self) -> u32 { |
||||||
|
assert!(self.code_block.code.len() < u32::MAX as usize); |
||||||
|
self.code_block.code.len() as u32 |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn emit(&mut self, opcode: Opcode, operands: &[u32]) { |
||||||
|
self.emit_opcode(opcode); |
||||||
|
for operand in operands { |
||||||
|
self.emit_u32(*operand); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn emit_u64(&mut self, value: u64) { |
||||||
|
self.code_block.code.extend(&u64_to_array(value)); |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn emit_u32(&mut self, value: u32) { |
||||||
|
self.code_block.code.extend(&u32_to_array(value)); |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn emit_u16(&mut self, value: u16) { |
||||||
|
self.code_block.code.extend(&u16_to_array(value)); |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn emit_opcode(&mut self, opcode: Opcode) { |
||||||
|
self.emit_u8(opcode as u8) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn emit_u8(&mut self, value: u8) { |
||||||
|
self.code_block.code.push(value); |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn emit_push_integer(&mut self, value: i32) { |
||||||
|
match value { |
||||||
|
0 => self.emit_opcode(Opcode::PushZero), |
||||||
|
1 => self.emit_opcode(Opcode::PushOne), |
||||||
|
x if x as i8 as i32 == x => { |
||||||
|
self.emit_opcode(Opcode::PushInt8); |
||||||
|
self.emit_u8(x as i8 as u8); |
||||||
|
} |
||||||
|
x if x as i16 as i32 == x => { |
||||||
|
self.emit_opcode(Opcode::PushInt16); |
||||||
|
self.emit_u16(x as i16 as u16); |
||||||
|
} |
||||||
|
x => self.emit(Opcode::PushInt32, &[x as _]), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn emit_push_literal(&mut self, liternal: Literal) { |
||||||
|
let index = self.get_or_insert_literal(liternal); |
||||||
|
self.emit(Opcode::PushLiteral, &[index]); |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn emit_push_rational(&mut self, value: f64) { |
||||||
|
if value.is_nan() { |
||||||
|
return self.emit_opcode(Opcode::PushNaN); |
||||||
|
} |
||||||
|
|
||||||
|
if value.is_infinite() { |
||||||
|
if value.is_sign_positive() { |
||||||
|
return self.emit_opcode(Opcode::PushPositiveInfinity); |
||||||
|
} else { |
||||||
|
return self.emit_opcode(Opcode::PushNegativeInfinity); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Check if the f64 value can fit in an i32.
|
||||||
|
#[allow(clippy::float_cmp)] |
||||||
|
if value as i32 as f64 == value { |
||||||
|
self.emit_push_integer(value as i32); |
||||||
|
} else { |
||||||
|
self.emit_opcode(Opcode::PushRational); |
||||||
|
self.emit_u64(value.to_bits()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn jump(&mut self) -> Label { |
||||||
|
let index = self.next_opcode_location(); |
||||||
|
self.emit(Opcode::Jump, &[Self::DUMMY_ADDRESS]); |
||||||
|
Label { index } |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn jump_if_false(&mut self) -> Label { |
||||||
|
let index = self.next_opcode_location(); |
||||||
|
self.emit(Opcode::JumpIfFalse, &[Self::DUMMY_ADDRESS]); |
||||||
|
|
||||||
|
Label { index } |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn jump_with_custom_opcode(&mut self, opcode: Opcode) -> Label { |
||||||
|
let index = self.next_opcode_location(); |
||||||
|
self.emit(opcode, &[Self::DUMMY_ADDRESS]); |
||||||
|
Label { index } |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn patch_jump_with_target(&mut self, label: Label, target: u32) { |
||||||
|
let Label { index } = label; |
||||||
|
|
||||||
|
let index = index as usize; |
||||||
|
|
||||||
|
let bytes = u32_to_array(target); |
||||||
|
self.code_block.code[index + 1] = bytes[0]; |
||||||
|
self.code_block.code[index + 2] = bytes[1]; |
||||||
|
self.code_block.code[index + 3] = bytes[2]; |
||||||
|
self.code_block.code[index + 4] = bytes[3]; |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn patch_jump(&mut self, label: Label) { |
||||||
|
let target = self.next_opcode_location(); |
||||||
|
self.patch_jump_with_target(label, target); |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn push_loop_control_info(&mut self, label: Option<Box<str>>, loop_start: u32) { |
||||||
|
self.loops.push(LoopControlInfo { |
||||||
|
label, |
||||||
|
loop_start, |
||||||
|
continues: Vec::new(), |
||||||
|
breaks: Vec::new(), |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn pop_loop_control_info(&mut self) { |
||||||
|
let loop_info = self.loops.pop().unwrap(); |
||||||
|
|
||||||
|
for label in loop_info.continues { |
||||||
|
self.patch_jump_with_target(label, loop_info.loop_start); |
||||||
|
} |
||||||
|
|
||||||
|
for label in loop_info.breaks { |
||||||
|
self.patch_jump(label); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn compile_access<'a>(&mut self, node: &'a Node) -> Access<'a> { |
||||||
|
match node { |
||||||
|
Node::Identifier(name) => Access::Variable { name }, |
||||||
|
Node::GetConstField(node) => Access::ByName { node }, |
||||||
|
Node::GetField(node) => Access::ByValue { node }, |
||||||
|
Node::This => Access::This, |
||||||
|
_ => unreachable!(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn access_get(&mut self, access: Access<'_>, use_expr: bool) { |
||||||
|
match access { |
||||||
|
Access::Variable { name } => { |
||||||
|
let index = self.get_or_insert_name(name.as_ref()); |
||||||
|
self.emit(Opcode::GetName, &[index]); |
||||||
|
} |
||||||
|
Access::ByName { node } => { |
||||||
|
let index = self.get_or_insert_name(node.field()); |
||||||
|
self.compile_expr(node.obj(), true); |
||||||
|
self.emit(Opcode::GetPropertyByName, &[index]); |
||||||
|
} |
||||||
|
Access::ByValue { node } => { |
||||||
|
self.compile_expr(node.field(), true); |
||||||
|
self.compile_expr(node.obj(), true); |
||||||
|
self.emit(Opcode::GetPropertyByValue, &[]); |
||||||
|
} |
||||||
|
Access::This => { |
||||||
|
self.emit(Opcode::This, &[]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if !use_expr { |
||||||
|
self.emit(Opcode::Pop, &[]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn access_set(&mut self, access: Access<'_>, expr: Option<&Node>, use_expr: bool) { |
||||||
|
if let Some(expr) = expr { |
||||||
|
self.compile_expr(expr, true); |
||||||
|
} |
||||||
|
|
||||||
|
if use_expr { |
||||||
|
self.emit(Opcode::Dup, &[]); |
||||||
|
} |
||||||
|
|
||||||
|
match access { |
||||||
|
Access::Variable { name } => { |
||||||
|
let index = self.get_or_insert_name(name.as_ref()); |
||||||
|
self.emit(Opcode::SetName, &[index]); |
||||||
|
} |
||||||
|
Access::ByName { node } => { |
||||||
|
self.compile_expr(node.obj(), true); |
||||||
|
let index = self.get_or_insert_name(node.field()); |
||||||
|
self.emit(Opcode::SetPropertyByName, &[index]); |
||||||
|
} |
||||||
|
Access::ByValue { node } => { |
||||||
|
self.compile_expr(node.field(), true); |
||||||
|
self.compile_expr(node.obj(), true); |
||||||
|
self.emit(Opcode::SetPropertyByValue, &[]); |
||||||
|
} |
||||||
|
Access::This => todo!("access_get 'this'"), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
pub fn compile_statement_list(&mut self, list: &StatementList, use_expr: bool) { |
||||||
|
for (i, node) in list.items().iter().enumerate() { |
||||||
|
if i + 1 == list.items().len() { |
||||||
|
self.compile_stmt(node, use_expr); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
self.compile_stmt(node, false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
pub fn compile_expr(&mut self, expr: &Node, use_expr: bool) { |
||||||
|
match expr { |
||||||
|
Node::Const(c) => { |
||||||
|
match c { |
||||||
|
Const::String(v) => self.emit_push_literal(Literal::String(v.as_ref().into())), |
||||||
|
Const::Int(v) => self.emit_push_integer(*v), |
||||||
|
Const::Num(v) => self.emit_push_rational(*v), |
||||||
|
Const::BigInt(v) => self.emit_push_literal(Literal::BigInt(v.clone().into())), |
||||||
|
Const::Bool(true) => self.emit(Opcode::PushTrue, &[]), |
||||||
|
Const::Bool(false) => self.emit(Opcode::PushFalse, &[]), |
||||||
|
Const::Null => self.emit(Opcode::PushNull, &[]), |
||||||
|
Const::Undefined => self.emit(Opcode::PushUndefined, &[]), |
||||||
|
} |
||||||
|
|
||||||
|
if !use_expr { |
||||||
|
self.emit(Opcode::Pop, &[]); |
||||||
|
} |
||||||
|
} |
||||||
|
Node::UnaryOp(unary) => { |
||||||
|
let opcode = match unary.op() { |
||||||
|
UnaryOp::IncrementPre => todo!(), |
||||||
|
UnaryOp::DecrementPre => todo!(), |
||||||
|
UnaryOp::IncrementPost => todo!(), |
||||||
|
UnaryOp::DecrementPost => todo!(), |
||||||
|
UnaryOp::Delete => todo!(), |
||||||
|
UnaryOp::Minus => Some(Opcode::Neg), |
||||||
|
UnaryOp::Plus => Some(Opcode::Pos), |
||||||
|
UnaryOp::Not => Some(Opcode::LogicalNot), |
||||||
|
UnaryOp::Tilde => Some(Opcode::BitNot), |
||||||
|
UnaryOp::TypeOf => Some(Opcode::TypeOf), |
||||||
|
UnaryOp::Void => Some(Opcode::Void), |
||||||
|
}; |
||||||
|
|
||||||
|
if let Some(opcode) = opcode { |
||||||
|
self.compile_expr(unary.target(), true); |
||||||
|
self.emit(opcode, &[]); |
||||||
|
|
||||||
|
if !use_expr { |
||||||
|
self.emit(Opcode::Pop, &[]); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
Node::BinOp(binary) => { |
||||||
|
self.compile_expr(binary.lhs(), true); |
||||||
|
match binary.op() { |
||||||
|
BinOp::Num(op) => { |
||||||
|
self.compile_expr(binary.rhs(), true); |
||||||
|
match op { |
||||||
|
NumOp::Add => self.emit_opcode(Opcode::Add), |
||||||
|
NumOp::Sub => self.emit_opcode(Opcode::Sub), |
||||||
|
NumOp::Div => self.emit_opcode(Opcode::Div), |
||||||
|
NumOp::Mul => self.emit_opcode(Opcode::Mul), |
||||||
|
NumOp::Exp => self.emit_opcode(Opcode::Pow), |
||||||
|
NumOp::Mod => self.emit_opcode(Opcode::Mod), |
||||||
|
} |
||||||
|
} |
||||||
|
BinOp::Bit(op) => { |
||||||
|
self.compile_expr(binary.rhs(), true); |
||||||
|
match op { |
||||||
|
BitOp::And => self.emit_opcode(Opcode::BitAnd), |
||||||
|
BitOp::Or => self.emit_opcode(Opcode::BitOr), |
||||||
|
BitOp::Xor => self.emit_opcode(Opcode::BitXor), |
||||||
|
BitOp::Shl => self.emit_opcode(Opcode::ShiftLeft), |
||||||
|
BitOp::Shr => self.emit_opcode(Opcode::ShiftRight), |
||||||
|
BitOp::UShr => self.emit_opcode(Opcode::UnsignedShiftRight), |
||||||
|
} |
||||||
|
} |
||||||
|
BinOp::Comp(op) => { |
||||||
|
self.compile_expr(binary.rhs(), true); |
||||||
|
match op { |
||||||
|
CompOp::Equal => self.emit_opcode(Opcode::Eq), |
||||||
|
CompOp::NotEqual => self.emit_opcode(Opcode::NotEq), |
||||||
|
CompOp::StrictEqual => self.emit_opcode(Opcode::StrictEq), |
||||||
|
CompOp::StrictNotEqual => self.emit_opcode(Opcode::StrictNotEq), |
||||||
|
CompOp::GreaterThan => self.emit_opcode(Opcode::GreaterThan), |
||||||
|
CompOp::GreaterThanOrEqual => self.emit_opcode(Opcode::GreaterThanOrEq), |
||||||
|
CompOp::LessThan => self.emit_opcode(Opcode::LessThan), |
||||||
|
CompOp::LessThanOrEqual => self.emit_opcode(Opcode::LessThanOrEq), |
||||||
|
CompOp::In => self.emit_opcode(Opcode::In), |
||||||
|
CompOp::InstanceOf => self.emit_opcode(Opcode::InstanceOf), |
||||||
|
} |
||||||
|
} |
||||||
|
BinOp::Log(op) => { |
||||||
|
match op { |
||||||
|
LogOp::And => { |
||||||
|
let exit = self.jump_with_custom_opcode(Opcode::LogicalAnd); |
||||||
|
self.compile_expr(binary.rhs(), true); |
||||||
|
self.emit(Opcode::ToBoolean, &[]); |
||||||
|
self.patch_jump(exit); |
||||||
|
} |
||||||
|
LogOp::Or => { |
||||||
|
let exit = self.jump_with_custom_opcode(Opcode::LogicalOr); |
||||||
|
self.compile_expr(binary.rhs(), true); |
||||||
|
self.emit(Opcode::ToBoolean, &[]); |
||||||
|
self.patch_jump(exit); |
||||||
|
} |
||||||
|
LogOp::Coalesce => { |
||||||
|
let exit = self.jump_with_custom_opcode(Opcode::Coalesce); |
||||||
|
self.compile_expr(binary.rhs(), true); |
||||||
|
self.patch_jump(exit); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
BinOp::Assign(op) => { |
||||||
|
let opcode = match op { |
||||||
|
AssignOp::Add => Some(Opcode::Add), |
||||||
|
AssignOp::Sub => Some(Opcode::Sub), |
||||||
|
AssignOp::Mul => Some(Opcode::Mul), |
||||||
|
AssignOp::Div => Some(Opcode::Div), |
||||||
|
AssignOp::Mod => Some(Opcode::Mod), |
||||||
|
AssignOp::Exp => Some(Opcode::Pow), |
||||||
|
AssignOp::And => Some(Opcode::BitAnd), |
||||||
|
AssignOp::Or => Some(Opcode::BitOr), |
||||||
|
AssignOp::Xor => Some(Opcode::BitXor), |
||||||
|
AssignOp::Shl => Some(Opcode::ShiftLeft), |
||||||
|
AssignOp::Shr => Some(Opcode::ShiftRight), |
||||||
|
AssignOp::Ushr => Some(Opcode::UnsignedShiftRight), |
||||||
|
AssignOp::BoolAnd => { |
||||||
|
let exit = self.jump_with_custom_opcode(Opcode::LogicalAnd); |
||||||
|
self.compile_expr(binary.rhs(), true); |
||||||
|
self.emit(Opcode::ToBoolean, &[]); |
||||||
|
self.patch_jump(exit); |
||||||
|
|
||||||
|
None |
||||||
|
} |
||||||
|
AssignOp::BoolOr => { |
||||||
|
let exit = self.jump_with_custom_opcode(Opcode::LogicalOr); |
||||||
|
self.compile_expr(binary.rhs(), true); |
||||||
|
self.emit(Opcode::ToBoolean, &[]); |
||||||
|
self.patch_jump(exit); |
||||||
|
|
||||||
|
None |
||||||
|
} |
||||||
|
AssignOp::Coalesce => { |
||||||
|
let exit = self.jump_with_custom_opcode(Opcode::Coalesce); |
||||||
|
self.compile_expr(binary.rhs(), true); |
||||||
|
self.patch_jump(exit); |
||||||
|
|
||||||
|
None |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
if let Some(opcode) = opcode { |
||||||
|
self.compile_expr(binary.rhs(), true); |
||||||
|
self.emit(opcode, &[]); |
||||||
|
} |
||||||
|
|
||||||
|
let access = self.compile_access(binary.lhs()); |
||||||
|
self.access_set(access, None, use_expr); |
||||||
|
} |
||||||
|
BinOp::Comma => { |
||||||
|
self.emit(Opcode::Pop, &[]); |
||||||
|
self.compile_expr(binary.rhs(), true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if !use_expr { |
||||||
|
self.emit(Opcode::Pop, &[]); |
||||||
|
} |
||||||
|
} |
||||||
|
Node::Object(object) => { |
||||||
|
if object.properties().is_empty() { |
||||||
|
self.emit(Opcode::PushEmptyObject, &[]); |
||||||
|
} else { |
||||||
|
todo!("object literal with properties"); |
||||||
|
} |
||||||
|
|
||||||
|
if !use_expr { |
||||||
|
self.emit(Opcode::Pop, &[]); |
||||||
|
} |
||||||
|
} |
||||||
|
Node::Identifier(name) => { |
||||||
|
let access = Access::Variable { name }; |
||||||
|
self.access_get(access, use_expr); |
||||||
|
} |
||||||
|
Node::Assign(assign) => { |
||||||
|
let access = self.compile_access(assign.lhs()); |
||||||
|
self.access_set(access, Some(assign.rhs()), use_expr); |
||||||
|
} |
||||||
|
Node::GetConstField(node) => { |
||||||
|
let access = Access::ByName { node }; |
||||||
|
self.access_get(access, use_expr); |
||||||
|
} |
||||||
|
Node::GetField(node) => { |
||||||
|
let access = Access::ByValue { node }; |
||||||
|
self.access_get(access, use_expr); |
||||||
|
} |
||||||
|
Node::ConditionalOp(op) => { |
||||||
|
self.compile_expr(op.cond(), true); |
||||||
|
let jelse = self.jump_if_false(); |
||||||
|
self.compile_expr(op.if_true(), true); |
||||||
|
let exit = self.jump(); |
||||||
|
self.patch_jump(jelse); |
||||||
|
self.compile_expr(op.if_false(), true); |
||||||
|
self.patch_jump(exit); |
||||||
|
|
||||||
|
if !use_expr { |
||||||
|
self.emit(Opcode::Pop, &[]); |
||||||
|
} |
||||||
|
} |
||||||
|
Node::ArrayDecl(array) => { |
||||||
|
let mut count = 0; |
||||||
|
for element in array.as_ref().iter().rev() { |
||||||
|
if let Node::Spread(_) = element { |
||||||
|
todo!("array with spread element"); |
||||||
|
} else { |
||||||
|
self.compile_expr(element, true); |
||||||
|
} |
||||||
|
count += 1; |
||||||
|
} |
||||||
|
self.emit(Opcode::PushNewArray, &[count]); |
||||||
|
|
||||||
|
if !use_expr { |
||||||
|
self.emit(Opcode::Pop, &[]); |
||||||
|
} |
||||||
|
} |
||||||
|
Node::This => { |
||||||
|
self.access_get(Access::This, use_expr); |
||||||
|
} |
||||||
|
expr => todo!("TODO compile: {}", expr), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
pub fn compile_stmt(&mut self, node: &Node, use_expr: bool) { |
||||||
|
match node { |
||||||
|
Node::VarDeclList(list) => { |
||||||
|
for decl in list.as_ref() { |
||||||
|
let index = self.get_or_insert_name(decl.name()); |
||||||
|
self.emit(Opcode::DefVar, &[index]); |
||||||
|
|
||||||
|
if let Some(expr) = decl.init() { |
||||||
|
self.compile_expr(expr, true); |
||||||
|
self.emit(Opcode::InitLexical, &[index]); |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
Node::LetDeclList(list) => { |
||||||
|
for decl in list.as_ref() { |
||||||
|
let index = self.get_or_insert_name(decl.name()); |
||||||
|
self.emit(Opcode::DefLet, &[index]); |
||||||
|
|
||||||
|
if let Some(expr) = decl.init() { |
||||||
|
self.compile_expr(expr, true); |
||||||
|
self.emit(Opcode::InitLexical, &[index]); |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
Node::ConstDeclList(list) => { |
||||||
|
for decl in list.as_ref() { |
||||||
|
let index = self.get_or_insert_name(decl.name()); |
||||||
|
self.emit(Opcode::DefConst, &[index]); |
||||||
|
|
||||||
|
if let Some(expr) = decl.init() { |
||||||
|
self.compile_expr(expr, true); |
||||||
|
self.emit(Opcode::InitLexical, &[index]); |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
Node::If(node) => { |
||||||
|
self.compile_expr(node.cond(), true); |
||||||
|
let jelse = self.jump_if_false(); |
||||||
|
|
||||||
|
self.compile_stmt(node.body(), false); |
||||||
|
|
||||||
|
match node.else_node() { |
||||||
|
None => { |
||||||
|
self.patch_jump(jelse); |
||||||
|
} |
||||||
|
Some(else_body) => { |
||||||
|
let exit = self.jump(); |
||||||
|
self.patch_jump(jelse); |
||||||
|
self.compile_stmt(else_body, false); |
||||||
|
self.patch_jump(exit); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
Node::WhileLoop(while_) => { |
||||||
|
let loop_start = self.next_opcode_location(); |
||||||
|
self.push_loop_control_info(while_.label().map(Into::into), loop_start); |
||||||
|
|
||||||
|
self.compile_expr(while_.cond(), true); |
||||||
|
let exit = self.jump_if_false(); |
||||||
|
self.compile_stmt(while_.body(), false); |
||||||
|
self.emit(Opcode::Jump, &[loop_start]); |
||||||
|
self.patch_jump(exit); |
||||||
|
|
||||||
|
self.pop_loop_control_info(); |
||||||
|
} |
||||||
|
Node::DoWhileLoop(do_while) => { |
||||||
|
let loop_start = self.next_opcode_location(); |
||||||
|
self.push_loop_control_info(do_while.label().map(Into::into), loop_start); |
||||||
|
|
||||||
|
self.compile_stmt(do_while.body(), false); |
||||||
|
|
||||||
|
self.compile_expr(do_while.cond(), true); |
||||||
|
self.emit(Opcode::JumpIfTrue, &[loop_start]); |
||||||
|
|
||||||
|
self.pop_loop_control_info(); |
||||||
|
} |
||||||
|
Node::Continue(node) => { |
||||||
|
let jump_label = self.jump(); |
||||||
|
if node.label().is_none() { |
||||||
|
self.loops.last_mut().unwrap().continues.push(jump_label); |
||||||
|
} else { |
||||||
|
for loop_ in self.loops.iter_mut().rev() { |
||||||
|
if loop_.label.as_deref() == node.label() { |
||||||
|
loop_.continues.push(jump_label); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
Node::Break(node) => { |
||||||
|
let jump_label = self.jump(); |
||||||
|
if node.label().is_none() { |
||||||
|
self.loops.last_mut().unwrap().breaks.push(jump_label); |
||||||
|
} else { |
||||||
|
for loop_ in self.loops.iter_mut().rev() { |
||||||
|
if loop_.label.as_deref() == node.label() { |
||||||
|
loop_.breaks.push(jump_label); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
Node::Block(block) => { |
||||||
|
for node in block.items() { |
||||||
|
self.compile_stmt(node, false); |
||||||
|
} |
||||||
|
} |
||||||
|
Node::Throw(throw) => { |
||||||
|
self.compile_expr(throw.expr(), true); |
||||||
|
self.emit(Opcode::Throw, &[]); |
||||||
|
} |
||||||
|
Node::Empty => {} |
||||||
|
expr => self.compile_expr(expr, use_expr), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
pub fn finish(self) -> CodeBlock { |
||||||
|
self.code_block |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,215 @@ |
|||||||
|
use crate::{value::RcString, vm::Opcode, Value}; |
||||||
|
|
||||||
|
use std::{convert::TryInto, fmt::Write, mem::size_of}; |
||||||
|
|
||||||
|
/// This represents wether an object can be read from [`CodeBlock`] code.
|
||||||
|
pub unsafe trait Readable {} |
||||||
|
|
||||||
|
unsafe impl Readable for u8 {} |
||||||
|
unsafe impl Readable for i8 {} |
||||||
|
unsafe impl Readable for u16 {} |
||||||
|
unsafe impl Readable for i16 {} |
||||||
|
unsafe impl Readable for u32 {} |
||||||
|
unsafe impl Readable for i32 {} |
||||||
|
unsafe impl Readable for u64 {} |
||||||
|
unsafe impl Readable for i64 {} |
||||||
|
unsafe impl Readable for f32 {} |
||||||
|
unsafe impl Readable for f64 {} |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
pub struct CodeBlock { |
||||||
|
/// Bytecode
|
||||||
|
pub(crate) code: Vec<u8>, |
||||||
|
|
||||||
|
/// Literals
|
||||||
|
pub(crate) literals: Vec<Value>, |
||||||
|
|
||||||
|
/// Variables names
|
||||||
|
pub(crate) names: Vec<RcString>, |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for CodeBlock { |
||||||
|
fn default() -> Self { |
||||||
|
Self::new() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl CodeBlock { |
||||||
|
pub fn new() -> Self { |
||||||
|
Self { |
||||||
|
code: Vec::new(), |
||||||
|
literals: Vec::new(), |
||||||
|
names: Vec::new(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Read type T from code.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Does not check if read happens out-of-bounds.
|
||||||
|
pub unsafe fn read_unchecked<T: Readable>(&self, offset: usize) -> T { |
||||||
|
// This has to be an unaligned read because we can't gurantee that
|
||||||
|
// the types are aligned.
|
||||||
|
self.code.as_ptr().add(offset).cast::<T>().read_unaligned() |
||||||
|
} |
||||||
|
|
||||||
|
/// Read type T from code.
|
||||||
|
pub fn read<T: Readable>(&self, offset: usize) -> T { |
||||||
|
assert!(offset + size_of::<T>() - 1 < self.code.len()); |
||||||
|
|
||||||
|
// Safety: We checked that it is not an out-of-bounds read,
|
||||||
|
// so this is safe.
|
||||||
|
unsafe { self.read_unchecked(offset) } |
||||||
|
} |
||||||
|
|
||||||
|
pub(crate) fn instruction_operands(&self, pc: &mut usize) -> String { |
||||||
|
let opcode: Opcode = self.code[*pc].try_into().unwrap(); |
||||||
|
*pc += size_of::<Opcode>(); |
||||||
|
match opcode { |
||||||
|
Opcode::PushInt8 => { |
||||||
|
let result = self.read::<i8>(*pc).to_string(); |
||||||
|
*pc += size_of::<i8>(); |
||||||
|
result |
||||||
|
} |
||||||
|
Opcode::PushInt16 => { |
||||||
|
let result = self.read::<i16>(*pc).to_string(); |
||||||
|
*pc += size_of::<i16>(); |
||||||
|
result |
||||||
|
} |
||||||
|
Opcode::PushInt32 => { |
||||||
|
let result = self.read::<i32>(*pc).to_string(); |
||||||
|
*pc += size_of::<i32>(); |
||||||
|
result |
||||||
|
} |
||||||
|
Opcode::PushRational => { |
||||||
|
let operand = self.read::<f64>(*pc); |
||||||
|
*pc += size_of::<f64>(); |
||||||
|
ryu_js::Buffer::new().format(operand).to_string() |
||||||
|
} |
||||||
|
Opcode::PushLiteral |
||||||
|
| Opcode::PushNewArray |
||||||
|
| Opcode::Jump |
||||||
|
| Opcode::JumpIfFalse |
||||||
|
| Opcode::JumpIfTrue |
||||||
|
| Opcode::LogicalAnd |
||||||
|
| Opcode::LogicalOr |
||||||
|
| Opcode::Coalesce => { |
||||||
|
let result = self.read::<u32>(*pc).to_string(); |
||||||
|
*pc += size_of::<u32>(); |
||||||
|
result |
||||||
|
} |
||||||
|
Opcode::DefVar |
||||||
|
| Opcode::DefLet |
||||||
|
| Opcode::DefConst |
||||||
|
| Opcode::InitLexical |
||||||
|
| Opcode::GetName |
||||||
|
| Opcode::SetName |
||||||
|
| Opcode::GetPropertyByName |
||||||
|
| Opcode::SetPropertyByName => { |
||||||
|
let operand = self.read::<u32>(*pc); |
||||||
|
*pc += size_of::<u32>(); |
||||||
|
format!("{:04}: '{}'", operand, self.names[operand as usize]) |
||||||
|
} |
||||||
|
Opcode::Pop |
||||||
|
| Opcode::Dup |
||||||
|
| Opcode::Swap |
||||||
|
| Opcode::PushZero |
||||||
|
| Opcode::PushOne |
||||||
|
| Opcode::PushNaN |
||||||
|
| Opcode::PushPositiveInfinity |
||||||
|
| Opcode::PushNegativeInfinity |
||||||
|
| Opcode::PushNull |
||||||
|
| Opcode::PushTrue |
||||||
|
| Opcode::PushFalse |
||||||
|
| Opcode::PushUndefined |
||||||
|
| Opcode::PushEmptyObject |
||||||
|
| Opcode::Add |
||||||
|
| Opcode::Sub |
||||||
|
| Opcode::Div |
||||||
|
| Opcode::Mul |
||||||
|
| Opcode::Mod |
||||||
|
| Opcode::Pow |
||||||
|
| Opcode::ShiftRight |
||||||
|
| Opcode::ShiftLeft |
||||||
|
| Opcode::UnsignedShiftRight |
||||||
|
| Opcode::BitOr |
||||||
|
| Opcode::BitAnd |
||||||
|
| Opcode::BitXor |
||||||
|
| Opcode::BitNot |
||||||
|
| Opcode::In |
||||||
|
| Opcode::Eq |
||||||
|
| Opcode::StrictEq |
||||||
|
| Opcode::NotEq |
||||||
|
| Opcode::StrictNotEq |
||||||
|
| Opcode::GreaterThan |
||||||
|
| Opcode::GreaterThanOrEq |
||||||
|
| Opcode::LessThan |
||||||
|
| Opcode::LessThanOrEq |
||||||
|
| Opcode::InstanceOf |
||||||
|
| Opcode::TypeOf |
||||||
|
| Opcode::Void |
||||||
|
| Opcode::LogicalNot |
||||||
|
| Opcode::Pos |
||||||
|
| Opcode::Neg |
||||||
|
| Opcode::GetPropertyByValue |
||||||
|
| Opcode::SetPropertyByValue |
||||||
|
| Opcode::ToBoolean |
||||||
|
| Opcode::Throw |
||||||
|
| Opcode::This |
||||||
|
| Opcode::Nop => String::new(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl std::fmt::Display for CodeBlock { |
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
||||||
|
f.write_str("Code: \n")?; |
||||||
|
|
||||||
|
writeln!(f, " Location Count Opcode Operands")?; |
||||||
|
let mut pc = 0; |
||||||
|
let mut count = 0; |
||||||
|
while pc < self.code.len() { |
||||||
|
let opcode: Opcode = self.code[pc].try_into().unwrap(); |
||||||
|
write!( |
||||||
|
f, |
||||||
|
" {:06} {:04} {:<20}", |
||||||
|
pc, |
||||||
|
count, |
||||||
|
opcode.as_str() |
||||||
|
)?; |
||||||
|
writeln!(f, "{}", self.instruction_operands(&mut pc))?; |
||||||
|
count += 1; |
||||||
|
} |
||||||
|
|
||||||
|
f.write_char('\n')?; |
||||||
|
|
||||||
|
f.write_str("Literals:\n")?; |
||||||
|
if !self.literals.is_empty() { |
||||||
|
for (i, value) in self.literals.iter().enumerate() { |
||||||
|
writeln!( |
||||||
|
f, |
||||||
|
" {:04}: <{}> {}", |
||||||
|
i, |
||||||
|
value.get_type().as_str(), |
||||||
|
value.display() |
||||||
|
)?; |
||||||
|
} |
||||||
|
} else { |
||||||
|
writeln!(f, " <empty>")?; |
||||||
|
} |
||||||
|
|
||||||
|
f.write_char('\n')?; |
||||||
|
|
||||||
|
f.write_str("Names:\n")?; |
||||||
|
if !self.names.is_empty() { |
||||||
|
for (i, value) in self.names.iter().enumerate() { |
||||||
|
writeln!(f, " {:04}: {}", i, value)?; |
||||||
|
} |
||||||
|
} else { |
||||||
|
writeln!(f, " <empty>")?; |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
@ -1,112 +0,0 @@ |
|||||||
use super::*; |
|
||||||
use crate::{syntax::ast::Const, syntax::ast::Node, value::RcBigInt, value::RcString}; |
|
||||||
|
|
||||||
#[derive(Debug, Default)] |
|
||||||
/// The compiler struct holds all the instructions.
|
|
||||||
pub struct Compiler { |
|
||||||
/// Vector of instructions
|
|
||||||
pub(super) instructions: Vec<Instruction>, |
|
||||||
/// The pool stores constant data that can be indexed with the opcodes and pushed on the stack
|
|
||||||
pub(super) pool: Vec<Value>, |
|
||||||
} |
|
||||||
|
|
||||||
impl Compiler { |
|
||||||
/// Add a new instruction.
|
|
||||||
pub fn add_instruction(&mut self, instr: Instruction) { |
|
||||||
self.instructions.push(instr); |
|
||||||
} |
|
||||||
|
|
||||||
/// This specilaized method puts the string value in the pool then adds an instructions which points to the correct index
|
|
||||||
pub fn add_string_instruction<S>(&mut self, string: S) |
|
||||||
where |
|
||||||
S: Into<RcString>, |
|
||||||
{ |
|
||||||
let index = self.pool.len(); |
|
||||||
self.add_instruction(Instruction::String(index)); |
|
||||||
self.pool.push(string.into().into()); |
|
||||||
} |
|
||||||
|
|
||||||
/// This specilaized method puts the BigInt value in the pool then adds an instructions which points to the correct index
|
|
||||||
pub fn add_bigint_instruction<B>(&mut self, bigint: B) |
|
||||||
where |
|
||||||
B: Into<RcBigInt>, |
|
||||||
{ |
|
||||||
let index = self.pool.len(); |
|
||||||
self.add_instruction(Instruction::BigInt(index)); |
|
||||||
self.pool.push(bigint.into().into()); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
pub(crate) trait CodeGen { |
|
||||||
fn compile(&self, compiler: &mut Compiler); |
|
||||||
} |
|
||||||
|
|
||||||
impl CodeGen for Node { |
|
||||||
fn compile(&self, compiler: &mut Compiler) { |
|
||||||
let _timer = BoaProfiler::global().start_event(&format!("Node ({})", &self), "codeGen"); |
|
||||||
match *self { |
|
||||||
Node::Const(Const::Undefined) => compiler.add_instruction(Instruction::Undefined), |
|
||||||
Node::Const(Const::Null) => compiler.add_instruction(Instruction::Null), |
|
||||||
Node::Const(Const::Bool(true)) => compiler.add_instruction(Instruction::True), |
|
||||||
Node::Const(Const::Bool(false)) => compiler.add_instruction(Instruction::False), |
|
||||||
Node::Const(Const::Num(num)) => compiler.add_instruction(Instruction::Rational(num)), |
|
||||||
Node::Const(Const::Int(num)) => match num { |
|
||||||
0 => compiler.add_instruction(Instruction::Zero), |
|
||||||
1 => compiler.add_instruction(Instruction::One), |
|
||||||
_ => compiler.add_instruction(Instruction::Int32(num)), |
|
||||||
}, |
|
||||||
Node::Const(Const::String(ref string)) => { |
|
||||||
compiler.add_string_instruction(string.clone()) |
|
||||||
} |
|
||||||
Node::Const(Const::BigInt(ref bigint)) => { |
|
||||||
compiler.add_bigint_instruction(bigint.clone()) |
|
||||||
} |
|
||||||
Node::BinOp(ref op) => op.compile(compiler), |
|
||||||
Node::UnaryOp(ref op) => op.compile(compiler), |
|
||||||
Node::VarDeclList(ref list) => { |
|
||||||
for var_decl in list.as_ref() { |
|
||||||
let name = var_decl.name(); |
|
||||||
let index = compiler.pool.len(); |
|
||||||
compiler.add_instruction(Instruction::DefVar(index)); |
|
||||||
compiler.pool.push(name.into()); |
|
||||||
|
|
||||||
if let Some(v) = var_decl.init() { |
|
||||||
v.compile(compiler); |
|
||||||
compiler.add_instruction(Instruction::InitLexical(index)) |
|
||||||
}; |
|
||||||
} |
|
||||||
} |
|
||||||
Node::LetDeclList(ref list) => { |
|
||||||
for let_decl in list.as_ref() { |
|
||||||
let name = let_decl.name(); |
|
||||||
let index = compiler.pool.len(); |
|
||||||
compiler.add_instruction(Instruction::DefLet(index)); |
|
||||||
compiler.pool.push(name.into()); |
|
||||||
|
|
||||||
// If name has a value we can init here too
|
|
||||||
if let Some(v) = let_decl.init() { |
|
||||||
v.compile(compiler); |
|
||||||
compiler.add_instruction(Instruction::InitLexical(index)) |
|
||||||
}; |
|
||||||
} |
|
||||||
} |
|
||||||
Node::ConstDeclList(ref list) => { |
|
||||||
for const_decl in list.as_ref() { |
|
||||||
let name = const_decl.name(); |
|
||||||
let index = compiler.pool.len(); |
|
||||||
compiler.add_instruction(Instruction::DefConst(index)); |
|
||||||
compiler.pool.push(name.into()); |
|
||||||
|
|
||||||
if let Some(v) = const_decl.init() { |
|
||||||
v.compile(compiler); |
|
||||||
compiler.add_instruction(Instruction::InitLexical(index)) |
|
||||||
}; |
|
||||||
} |
|
||||||
} |
|
||||||
Node::Identifier(ref name) => name.compile(compiler), |
|
||||||
Node::Object(ref obj) => obj.compile(compiler), |
|
||||||
|
|
||||||
_ => unimplemented!(), |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,127 +0,0 @@ |
|||||||
#[derive(Debug)] |
|
||||||
pub enum Instruction { |
|
||||||
Undefined, |
|
||||||
Null, |
|
||||||
True, |
|
||||||
False, |
|
||||||
Zero, |
|
||||||
One, |
|
||||||
String(usize), |
|
||||||
BigInt(usize), |
|
||||||
|
|
||||||
/// Loads an i32 onto the stack
|
|
||||||
Int32(i32), |
|
||||||
|
|
||||||
/// Loads an f64 onto the stack
|
|
||||||
Rational(f64), |
|
||||||
|
|
||||||
/// Adds the values from destination and source and stores the result in destination
|
|
||||||
Add, |
|
||||||
|
|
||||||
/// subtracts the values from destination and source and stores the result in destination
|
|
||||||
Sub, |
|
||||||
|
|
||||||
/// Multiplies the values from destination and source and stores the result in destination
|
|
||||||
Mul, |
|
||||||
|
|
||||||
/// Divides the values from destination and source and stores the result in destination
|
|
||||||
Div, |
|
||||||
|
|
||||||
Pow, |
|
||||||
|
|
||||||
Mod, |
|
||||||
|
|
||||||
BitAnd, |
|
||||||
BitOr, |
|
||||||
BitXor, |
|
||||||
Shl, |
|
||||||
Shr, |
|
||||||
UShr, |
|
||||||
|
|
||||||
Eq, |
|
||||||
NotEq, |
|
||||||
StrictEq, |
|
||||||
StrictNotEq, |
|
||||||
|
|
||||||
Gt, |
|
||||||
Ge, |
|
||||||
Lt, |
|
||||||
Le, |
|
||||||
|
|
||||||
In, |
|
||||||
InstanceOf, |
|
||||||
|
|
||||||
Void, |
|
||||||
TypeOf, |
|
||||||
Pos, |
|
||||||
Neg, |
|
||||||
BitNot, |
|
||||||
Not, |
|
||||||
|
|
||||||
/// The usize is the index of the variable name in the pool
|
|
||||||
DefVar(usize), |
|
||||||
/// The usize is the index of the variable name in the pool
|
|
||||||
DefLet(usize), |
|
||||||
/// The usize is the index of the variable name in the pool
|
|
||||||
DefConst(usize), |
|
||||||
/// The usize is the index of the value to initiate the variable with in the pool
|
|
||||||
InitLexical(usize), |
|
||||||
|
|
||||||
// Binding values
|
|
||||||
/// Find a binding on the environment chain and push its value.
|
|
||||||
GetName(String), |
|
||||||
// Objects
|
|
||||||
// Create and push a new object onto the stack
|
|
||||||
NewObject, |
|
||||||
} |
|
||||||
|
|
||||||
impl std::fmt::Display for Instruction { |
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
|
||||||
match *self { |
|
||||||
Self::Undefined => write!(f, "Undefined"), |
|
||||||
Self::Null => write!(f, "Null"), |
|
||||||
Self::True => write!(f, "True"), |
|
||||||
Self::False => write!(f, "False"), |
|
||||||
Self::Zero => write!(f, "Zero"), |
|
||||||
Self::One => write!(f, "One"), |
|
||||||
Self::String(usize) => write!(f, "String({})", usize), |
|
||||||
Self::BigInt(usize) => write!(f, "BigInt({})", usize), |
|
||||||
Self::Int32(i32) => write!(f, "Int32({})", i32), |
|
||||||
Self::Rational(f64) => write!(f, "Rational({})", f64), |
|
||||||
Self::Add => write!(f, "Add"), |
|
||||||
Self::Sub => write!(f, "Sub"), |
|
||||||
Self::Mul => write!(f, "Mul"), |
|
||||||
Self::Div => write!(f, "Div"), |
|
||||||
Self::Pow => write!(f, "Pow"), |
|
||||||
Self::Mod => write!(f, "Mod"), |
|
||||||
Self::BitAnd => write!(f, "BitAnd"), |
|
||||||
Self::BitOr => write!(f, "BitOr"), |
|
||||||
Self::BitXor => write!(f, "BitXor"), |
|
||||||
Self::GetName(ref name) => write!(f, "GetName({})", name), |
|
||||||
Self::Shl => write!(f, "Shl"), |
|
||||||
Self::Shr => write!(f, "Shr"), |
|
||||||
Self::UShr => write!(f, "UShr"), |
|
||||||
Self::Eq => write!(f, "Eq"), |
|
||||||
Self::NotEq => write!(f, "NotEq"), |
|
||||||
Self::StrictEq => write!(f, "StrictEq"), |
|
||||||
Self::StrictNotEq => write!(f, "StrictNotEq"), |
|
||||||
Self::Gt => write!(f, "Gt"), |
|
||||||
Self::Ge => write!(f, "Ge"), |
|
||||||
Self::Lt => write!(f, "Lt"), |
|
||||||
Self::Le => write!(f, "Le"), |
|
||||||
Self::In => write!(f, "In"), |
|
||||||
Self::InstanceOf => write!(f, "InstanceOf"), |
|
||||||
Self::Void => write!(f, "Void"), |
|
||||||
Self::TypeOf => write!(f, "TypeOf"), |
|
||||||
Self::Pos => write!(f, "Pos"), |
|
||||||
Self::Neg => write!(f, "Neg"), |
|
||||||
Self::BitNot => write!(f, "BitNot"), |
|
||||||
Self::Not => write!(f, "Not"), |
|
||||||
Self::DefVar(name) => write!(f, "DefVar({})", name), |
|
||||||
Self::DefLet(name) => write!(f, "DefLet({})", name), |
|
||||||
Self::DefConst(name) => write!(f, "DefConst({})", name), |
|
||||||
Self::InitLexical(value) => write!(f, "InitLexical({})", value), |
|
||||||
Self::NewObject => write!(f, "NewObject"), |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,614 @@ |
|||||||
|
use std::convert::TryFrom; |
||||||
|
|
||||||
|
/// The opcodes of the vm.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
#[repr(u8)] |
||||||
|
pub enum Opcode { |
||||||
|
/// Pop the top value from the stack.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: value **=>**
|
||||||
|
Pop, |
||||||
|
|
||||||
|
/// Push a copy of the top value on the stack.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: value **=>** value, value
|
||||||
|
Dup, |
||||||
|
|
||||||
|
/// Swap the top two values on the stack.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: v1, v2 **=>** v2, v1
|
||||||
|
Swap, |
||||||
|
|
||||||
|
/// Push integer `0` on the stack.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: **=>** 0
|
||||||
|
PushZero, |
||||||
|
|
||||||
|
/// Push integer `1` on the stack.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: **=>** 1
|
||||||
|
PushOne, |
||||||
|
|
||||||
|
/// Push `i8` value on the stack.
|
||||||
|
///
|
||||||
|
/// Operands: value: `i8`
|
||||||
|
///
|
||||||
|
/// Stack: **=>** value
|
||||||
|
PushInt8, |
||||||
|
|
||||||
|
/// Push i16 value on the stack.
|
||||||
|
///
|
||||||
|
/// Operands: value: `i16`
|
||||||
|
///
|
||||||
|
/// Stack: **=>** value
|
||||||
|
PushInt16, |
||||||
|
|
||||||
|
/// Push i32 value on the stack.
|
||||||
|
///
|
||||||
|
/// Operands: value: `i32`
|
||||||
|
///
|
||||||
|
/// Stack: **=>** value
|
||||||
|
PushInt32, |
||||||
|
|
||||||
|
/// Push `f64` value on the stack.
|
||||||
|
///
|
||||||
|
/// Operands: value: `f64`
|
||||||
|
///
|
||||||
|
/// Stack: **=>** value
|
||||||
|
PushRational, |
||||||
|
|
||||||
|
/// Push `NaN` teger on the stack.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: **=>** `NaN`
|
||||||
|
PushNaN, |
||||||
|
|
||||||
|
/// Push `Infinity` value on the stack.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: **=>** `Infinity`
|
||||||
|
PushPositiveInfinity, |
||||||
|
|
||||||
|
/// Push `-Infinity` value on the stack.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: **=>** `-Infinity`
|
||||||
|
PushNegativeInfinity, |
||||||
|
|
||||||
|
/// Push `null` value on the stack.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: **=>** `null`
|
||||||
|
PushNull, |
||||||
|
|
||||||
|
/// Push `true` value on the stack.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: **=>** `true`
|
||||||
|
PushTrue, |
||||||
|
|
||||||
|
/// Push `false` value on the stack.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: **=>** `false`
|
||||||
|
PushFalse, |
||||||
|
|
||||||
|
/// Push `undefined` value on the stack.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: **=>** `undefined`
|
||||||
|
PushUndefined, |
||||||
|
|
||||||
|
/// Push literal value on the stack.
|
||||||
|
///
|
||||||
|
/// Like strings and bigints. The index oprand is used to index into the `literals`
|
||||||
|
/// array to get the value.
|
||||||
|
///
|
||||||
|
/// Operands: index: `u32`
|
||||||
|
///
|
||||||
|
/// Stack: **=>** (`literals[index]`)
|
||||||
|
PushLiteral, |
||||||
|
|
||||||
|
/// Push empty object `{}` value on the stack.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: **=>** object
|
||||||
|
PushEmptyObject, |
||||||
|
|
||||||
|
/// Push array object `{}` value on the stack.
|
||||||
|
///
|
||||||
|
/// Operands: n: `u32`
|
||||||
|
///
|
||||||
|
/// Stack: v1, v1, ... vn **=>** [v1, v2, ..., vn]
|
||||||
|
PushNewArray, |
||||||
|
|
||||||
|
/// Binary `+` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs + rhs)
|
||||||
|
Add, |
||||||
|
|
||||||
|
/// Binary `-` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs - rhs)
|
||||||
|
Sub, |
||||||
|
|
||||||
|
/// Binary `/` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs / rhs)
|
||||||
|
Div, |
||||||
|
|
||||||
|
/// Binary `*` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs * rhs)
|
||||||
|
Mul, |
||||||
|
|
||||||
|
/// Binary `%` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs % rhs)
|
||||||
|
Mod, |
||||||
|
|
||||||
|
/// Binary `**` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs ** rhs)
|
||||||
|
Pow, |
||||||
|
|
||||||
|
/// Binary `>>` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs >> rhs)
|
||||||
|
ShiftRight, |
||||||
|
|
||||||
|
/// Binary `<<` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs << rhs)
|
||||||
|
ShiftLeft, |
||||||
|
|
||||||
|
/// Binary `>>>` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs >>> rhs)
|
||||||
|
UnsignedShiftRight, |
||||||
|
|
||||||
|
/// Binary bitwise `|` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs | rhs)
|
||||||
|
BitOr, |
||||||
|
|
||||||
|
/// Binary bitwise `&` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs & rhs)
|
||||||
|
BitAnd, |
||||||
|
|
||||||
|
/// Binary bitwise `^` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs ^ rhs)
|
||||||
|
BitXor, |
||||||
|
|
||||||
|
/// Unary bitwise `~` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: value **=>** ~value
|
||||||
|
BitNot, |
||||||
|
|
||||||
|
/// Binary `in` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs `in` rhs)
|
||||||
|
In, |
||||||
|
|
||||||
|
/// Binary `==` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs `==` rhs)
|
||||||
|
Eq, |
||||||
|
|
||||||
|
/// Binary `===` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs `===` rhs)
|
||||||
|
StrictEq, |
||||||
|
|
||||||
|
/// Binary `!=` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs `!=` rhs)
|
||||||
|
NotEq, |
||||||
|
|
||||||
|
/// Binary `!==` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs `!==` rhs)
|
||||||
|
StrictNotEq, |
||||||
|
|
||||||
|
/// Binary `>` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs > rhs)
|
||||||
|
GreaterThan, |
||||||
|
|
||||||
|
/// Binary `>=` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs >= rhs)
|
||||||
|
GreaterThanOrEq, |
||||||
|
|
||||||
|
/// Binary `<` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs < rhs)
|
||||||
|
LessThan, |
||||||
|
|
||||||
|
/// Binary `<=` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs <= rhs)
|
||||||
|
LessThanOrEq, |
||||||
|
|
||||||
|
/// Binary `instanceof` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs instanceof rhs)
|
||||||
|
InstanceOf, |
||||||
|
|
||||||
|
/// Binary logical `&&` operator.
|
||||||
|
///
|
||||||
|
/// This is a short-circit operator, if the `lhs` value is `false`, then it jumps to `exit` address.
|
||||||
|
///
|
||||||
|
/// Operands: exit: `u32`
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs && rhs)
|
||||||
|
LogicalAnd, |
||||||
|
|
||||||
|
/// Binary logical `||` operator.
|
||||||
|
///
|
||||||
|
/// This is a short-circit operator, if the `lhs` value is `true`, then it jumps to `exit` address.
|
||||||
|
///
|
||||||
|
/// Operands: exit: `u32`
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs || rhs)
|
||||||
|
LogicalOr, |
||||||
|
|
||||||
|
/// Binary `??` operator.
|
||||||
|
///
|
||||||
|
/// This is a short-circit operator, if the `lhs` value is **not** `null` or `undefined`,
|
||||||
|
/// then it jumps to `exit` address.
|
||||||
|
///
|
||||||
|
/// Operands: exit: `u32`
|
||||||
|
///
|
||||||
|
/// Stack: lhs, rhs **=>** (lhs && rhs)
|
||||||
|
Coalesce, |
||||||
|
|
||||||
|
/// Unary `typeof` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: value **=>** (`typeof` value)
|
||||||
|
TypeOf, |
||||||
|
|
||||||
|
/// Unary `void` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: value **=>** `undefined`
|
||||||
|
Void, |
||||||
|
|
||||||
|
/// Unary logical `!` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: value **=>** (!value)
|
||||||
|
LogicalNot, |
||||||
|
|
||||||
|
/// Unary `+` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: value **=>** (+value)
|
||||||
|
Pos, |
||||||
|
|
||||||
|
/// Unary `-` operator.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: value **=>** (-value)
|
||||||
|
Neg, |
||||||
|
|
||||||
|
/// Declate `var` type variable.
|
||||||
|
///
|
||||||
|
/// Operands: name_index: `u32`
|
||||||
|
///
|
||||||
|
/// Stack: **=>**
|
||||||
|
DefVar, |
||||||
|
|
||||||
|
/// Declate `let` type variable.
|
||||||
|
///
|
||||||
|
/// Operands: name_index: `u32`
|
||||||
|
///
|
||||||
|
/// Stack: **=>**
|
||||||
|
DefLet, |
||||||
|
|
||||||
|
/// Declate `const` type variable.
|
||||||
|
///
|
||||||
|
/// Operands: name_index: `u32`
|
||||||
|
///
|
||||||
|
/// Stack: **=>**
|
||||||
|
DefConst, |
||||||
|
|
||||||
|
/// Initialize a lexical binding.
|
||||||
|
///
|
||||||
|
/// Operands: name_index: `u32`
|
||||||
|
///
|
||||||
|
/// Stack: **=>**
|
||||||
|
InitLexical, |
||||||
|
|
||||||
|
/// Find a binding on the environment chain and push its value.
|
||||||
|
///
|
||||||
|
/// Operands: name_index: `u32`
|
||||||
|
///
|
||||||
|
/// Stack: **=>** value
|
||||||
|
GetName, |
||||||
|
|
||||||
|
/// Find a binding on the environment chain and assign its value.
|
||||||
|
///
|
||||||
|
/// Operands: name_index: `u32`
|
||||||
|
///
|
||||||
|
/// Stack: value **=>**
|
||||||
|
SetName, |
||||||
|
|
||||||
|
/// Get a property by name from an object an push it on the stack.
|
||||||
|
///
|
||||||
|
/// Like `object.name`
|
||||||
|
///
|
||||||
|
/// Operands: name_index: `u32`
|
||||||
|
///
|
||||||
|
/// Stack: object **=>** value
|
||||||
|
GetPropertyByName, |
||||||
|
|
||||||
|
/// Get a property by value from an object an push it on the stack.
|
||||||
|
///
|
||||||
|
/// Like `object[key]`
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: key, object **=>** value
|
||||||
|
GetPropertyByValue, |
||||||
|
|
||||||
|
/// Sets a property by name of an object.
|
||||||
|
///
|
||||||
|
/// Like `object.name = value`
|
||||||
|
///
|
||||||
|
/// Operands: name_index: `u32`
|
||||||
|
///
|
||||||
|
/// Stack: value, object **=>**
|
||||||
|
SetPropertyByName, |
||||||
|
|
||||||
|
/// Sets a property by value of an object.
|
||||||
|
///
|
||||||
|
/// Like `object[key] = value`
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: value, key, object **=>**
|
||||||
|
SetPropertyByValue, |
||||||
|
|
||||||
|
/// Unconditional jump to address.
|
||||||
|
///
|
||||||
|
/// Operands: address: `u32`
|
||||||
|
/// Stack: **=>**
|
||||||
|
Jump, |
||||||
|
|
||||||
|
/// Constional jump to address.
|
||||||
|
///
|
||||||
|
/// If the value popped is [`falsy`][falsy] then jump to `address`.
|
||||||
|
///
|
||||||
|
/// Operands: address: `u32`
|
||||||
|
///
|
||||||
|
/// Stack: cond **=>**
|
||||||
|
///
|
||||||
|
/// [falsy]: https://developer.mozilla.org/en-US/docs/Glossary/Falsy
|
||||||
|
JumpIfFalse, |
||||||
|
|
||||||
|
/// Constional jump to address.
|
||||||
|
///
|
||||||
|
/// If the value popped is [`truthy`][truthy] then jump to `address`.
|
||||||
|
///
|
||||||
|
/// Operands: address: `u32`
|
||||||
|
///
|
||||||
|
/// Stack: cond **=>**
|
||||||
|
///
|
||||||
|
/// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy
|
||||||
|
JumpIfTrue, |
||||||
|
|
||||||
|
/// Throw exception
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: `exc` **=>**
|
||||||
|
Throw, |
||||||
|
|
||||||
|
/// Pops value converts it to boolean and pushes it back.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: value **=>** (`ToBoolean(value)`)
|
||||||
|
ToBoolean, |
||||||
|
|
||||||
|
/// Pushes `this` value
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: **=>** `this`
|
||||||
|
This, |
||||||
|
|
||||||
|
/// No-operation instruction, does nothing.
|
||||||
|
///
|
||||||
|
/// Operands:
|
||||||
|
///
|
||||||
|
/// Stack: **=>**
|
||||||
|
// Safety: Must be last in the list since, we use this for range checking
|
||||||
|
// in TryFrom<u8> impl.
|
||||||
|
Nop, |
||||||
|
} |
||||||
|
|
||||||
|
impl Opcode { |
||||||
|
/// Create opcode from `u8` byte.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Does not check if `u8` type is a valid `Opcode`.
|
||||||
|
pub unsafe fn from_raw(value: u8) -> Self { |
||||||
|
std::mem::transmute(value) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn as_str(self) -> &'static str { |
||||||
|
match self { |
||||||
|
Opcode::Pop => "Pop", |
||||||
|
Opcode::Dup => "Dup", |
||||||
|
Opcode::Swap => "Swap", |
||||||
|
Opcode::PushZero => "PushZero", |
||||||
|
Opcode::PushOne => "PushOne", |
||||||
|
Opcode::PushInt8 => "PushInt8", |
||||||
|
Opcode::PushInt16 => "PushInt16", |
||||||
|
Opcode::PushInt32 => "PushInt32", |
||||||
|
Opcode::PushRational => "PushRational", |
||||||
|
Opcode::PushNaN => "PushNaN", |
||||||
|
Opcode::PushPositiveInfinity => "PushPositiveInfinity", |
||||||
|
Opcode::PushNegativeInfinity => "PushNegativeInfinity", |
||||||
|
Opcode::PushNull => "PushNull", |
||||||
|
Opcode::PushTrue => "PushTrue", |
||||||
|
Opcode::PushFalse => "PushFalse", |
||||||
|
Opcode::PushUndefined => "PushUndefined", |
||||||
|
Opcode::PushLiteral => "PushLiteral", |
||||||
|
Opcode::PushEmptyObject => "PushEmptyObject", |
||||||
|
Opcode::PushNewArray => "PushNewArray", |
||||||
|
Opcode::Add => "Add", |
||||||
|
Opcode::Sub => "Sub", |
||||||
|
Opcode::Div => "Div", |
||||||
|
Opcode::Mul => "Mul", |
||||||
|
Opcode::Mod => "Mod", |
||||||
|
Opcode::Pow => "Pow", |
||||||
|
Opcode::ShiftRight => "ShiftRight", |
||||||
|
Opcode::ShiftLeft => "ShiftLeft", |
||||||
|
Opcode::UnsignedShiftRight => "UnsignedShiftRight", |
||||||
|
Opcode::BitOr => "BitOr", |
||||||
|
Opcode::BitAnd => "BitAnd", |
||||||
|
Opcode::BitXor => "BitXor", |
||||||
|
Opcode::BitNot => "BitNot", |
||||||
|
Opcode::In => "In", |
||||||
|
Opcode::Eq => "Eq", |
||||||
|
Opcode::StrictEq => "StrictEq", |
||||||
|
Opcode::NotEq => "NotEq", |
||||||
|
Opcode::StrictNotEq => "StrictNotEq", |
||||||
|
Opcode::GreaterThan => "GreaterThan", |
||||||
|
Opcode::GreaterThanOrEq => "GreaterThanOrEq", |
||||||
|
Opcode::LessThan => "LessThan", |
||||||
|
Opcode::LessThanOrEq => "LessThanOrEq", |
||||||
|
Opcode::InstanceOf => "InstanceOf", |
||||||
|
Opcode::TypeOf => "TypeOf", |
||||||
|
Opcode::Void => "Void", |
||||||
|
Opcode::LogicalNot => "LogicalNot", |
||||||
|
Opcode::LogicalAnd => "LogicalAnd", |
||||||
|
Opcode::LogicalOr => "LogicalOr", |
||||||
|
Opcode::Coalesce => "Coalesce", |
||||||
|
Opcode::Pos => "Pos", |
||||||
|
Opcode::Neg => "Neg", |
||||||
|
Opcode::DefVar => "DefVar", |
||||||
|
Opcode::DefLet => "DefLet", |
||||||
|
Opcode::DefConst => "DefConst", |
||||||
|
Opcode::InitLexical => "InitLexical", |
||||||
|
Opcode::GetName => "GetName", |
||||||
|
Opcode::SetName => "SetName", |
||||||
|
Opcode::GetPropertyByName => "GetPropertyByName", |
||||||
|
Opcode::GetPropertyByValue => "GetPropertyByValue", |
||||||
|
Opcode::SetPropertyByName => "SetPropertyByName", |
||||||
|
Opcode::SetPropertyByValue => "SetPropertyByValue", |
||||||
|
Opcode::Jump => "Jump", |
||||||
|
Opcode::JumpIfFalse => "JumpIfFalse", |
||||||
|
Opcode::JumpIfTrue => "JumpIfTrue", |
||||||
|
Opcode::Throw => "Throw", |
||||||
|
Opcode::ToBoolean => "ToBoolean", |
||||||
|
Opcode::This => "This", |
||||||
|
Opcode::Nop => "Nop", |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)] |
||||||
|
pub struct InvalidOpcodeError { |
||||||
|
value: u8, |
||||||
|
} |
||||||
|
|
||||||
|
impl std::fmt::Display for InvalidOpcodeError { |
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
||||||
|
write!(f, "invalid opcode: {:#04x}", self.value) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl std::error::Error for InvalidOpcodeError {} |
||||||
|
|
||||||
|
impl TryFrom<u8> for Opcode { |
||||||
|
type Error = InvalidOpcodeError; |
||||||
|
|
||||||
|
fn try_from(value: u8) -> Result<Self, Self::Error> { |
||||||
|
if value > Self::Nop as u8 { |
||||||
|
return Err(InvalidOpcodeError { value }); |
||||||
|
} |
||||||
|
|
||||||
|
// Safety: we already checked if it is in the Opcode range,
|
||||||
|
// so this is safe.
|
||||||
|
let opcode = unsafe { Self::from_raw(value) }; |
||||||
|
|
||||||
|
Ok(opcode) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue