//! Low-level bytecode emitter, used by ast_builder. //! //! This API makes it easy to emit correct individual bytecode instructions. // Most of this functionality isn't used yet. #![allow(dead_code)] use ast::source_atom_set::SourceAtomSetIndex; use byteorder::{ByteOrder, LittleEndian}; use std::cmp; use std::collections::HashMap; use std::convert::TryFrom; use std::convert::TryInto; use std::fmt; use stencil::bytecode_offset::{BytecodeOffset, BytecodeOffsetDiff}; use stencil::frame_slot::FrameSlot; use stencil::gcthings::{GCThingIndex, GCThingList}; use stencil::opcode::Opcode; use stencil::regexp::RegExpIndex; use stencil::scope::ScopeIndex; use stencil::scope_notes::{ScopeNoteIndex, ScopeNoteList}; use stencil::script::{ ImmutableScriptData, ImmutableScriptDataList, ScriptStencil, ScriptStencilIndex, SourceExtent, }; // WARNING // The following section is generated by update_stencil.py. // Do mot modify manually. // // @@@@ BEGIN TYPES @@@@ #[derive(Debug, Clone, Copy)] pub enum AsyncFunctionResolveKind { Fulfill = 0, Reject = 1, } #[derive(Debug, Clone, Copy)] pub enum CheckIsObjectKind { IteratorNext = 0, IteratorReturn = 1, IteratorThrow = 2, GetIterator = 3, GetAsyncIterator = 4, } #[derive(Debug, Clone, Copy)] pub enum CompletionKind { Normal = 0, Return = 1, Throw = 2, } #[derive(Debug, Clone, Copy)] pub enum FunctionPrefixKind { None = 0, Get = 1, Set = 2, } #[derive(Debug, Clone, Copy)] pub enum GeneratorResumeKind { Next = 0, Throw = 1, Return = 2, } #[derive(Debug, Clone, Copy)] pub enum ThrowMsgKind { AssignToCall = 0, IteratorNoThrow = 1, CantDeleteSuper = 2, PrivateDoubleInit = 3, PrivateBrandDoubleInit = 4, MissingPrivateOnGet = 5, MissingPrivateOnSet = 6, AssignToPrivateMethod = 7, DecoratorInvalidReturnType = 8, } #[derive(Debug, Clone, Copy)] pub enum ThrowCondition { ThrowHas = 0, ThrowHasNot = 1, OnlyCheckRhs = 2, } #[derive(Debug, Clone, Copy)] pub enum TryNoteKind { Catch = 0, Finally = 1, ForIn = 2, Destructuring = 3, ForOf = 4, ForOfIterClose = 5, Loop = 6, } #[derive(Debug, Clone, Copy)] pub enum SymbolCode { IsConcatSpreadable = 0, Iterator = 1, Match = 2, Replace = 3, Search = 4, Species = 5, HasInstance = 6, Split = 7, ToPrimitive = 8, ToStringTag = 9, Unscopables = 10, AsyncIterator = 11, MatchAll = 12, } #[derive(Debug, Clone, Copy)] pub enum SrcNoteType { Null = 0, AssignOp = 1, ColSpan = 2, NewLine = 3, SetLine = 4, Breakpoint = 5, StepSep = 6, Unused7 = 7, XDelta = 8, } // @@@@ END TYPES @@@@ #[allow(non_camel_case_types)] pub type u24 = u32; /// Low-level bytecode emitter. pub struct InstructionWriter { bytecode: Vec, /// To de-duplicate atoms in gcthings list, note the index for each atom. atom_to_gcindex_map: HashMap, gcthings: GCThingList, scope_notes: ScopeNoteList, last_jump_target_offset: Option, main_offset: BytecodeOffset, /// The maximum number of fixed frame slots. max_fixed_slots: FrameSlot, /// Stack depth after the instructions emitted so far. stack_depth: usize, /// Maximum stack_depth at any point in the instructions emitted so far. maximum_stack_depth: usize, body_scope_index: Option, /// Number of JOF_IC instructions emitted so far. num_ic_entries: usize, } #[derive(Debug)] pub struct EmitOptions { pub no_script_rval: bool, pub extent: SourceExtent, } impl EmitOptions { pub fn new(extent: SourceExtent) -> Self { Self { no_script_rval: false, extent, } } } /// The error of bytecode-compilation. #[derive(Clone, Debug)] pub enum EmitError { NotImplemented(&'static str), } impl fmt::Display for EmitError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { EmitError::NotImplemented(message) => write!(f, "not implemented: {}", message), } } } impl InstructionWriter { pub fn new() -> Self { Self { bytecode: Vec::new(), gcthings: GCThingList::new(), atom_to_gcindex_map: HashMap::new(), scope_notes: ScopeNoteList::new(), last_jump_target_offset: None, main_offset: BytecodeOffset::from(0usize), max_fixed_slots: FrameSlot::new(0), stack_depth: 0, maximum_stack_depth: 0, body_scope_index: None, num_ic_entries: 0, } } fn write_i8(&mut self, value: i8) { self.write_u8(value as u8); } fn write_u8(&mut self, value: u8) { self.bytecode.push(value); } fn write_u16(&mut self, value: u16) { self.bytecode.extend_from_slice(&value.to_le_bytes()); } fn write_u24(&mut self, value: u24) { let slice = value.to_le_bytes(); assert!(slice.len() == 4 && slice[3] == 0); self.bytecode.extend_from_slice(&slice[0..3]); } fn write_i32(&mut self, value: i32) { self.bytecode.extend_from_slice(&value.to_le_bytes()); } fn write_u32(&mut self, value: u32) { self.bytecode.extend_from_slice(&value.to_le_bytes()); } fn write_g_c_thing_index(&mut self, value: GCThingIndex) { self.write_u32(usize::from(value) as u32); } fn write_offset(&mut self, offset: i32) { self.write_i32(offset); } fn write_bytecode_offset_diff(&mut self, offset: BytecodeOffsetDiff) { self.write_i32(i32::from(offset)); } fn write_f64(&mut self, val: f64) { self.bytecode .extend_from_slice(&val.to_bits().to_le_bytes()); } fn write_ic_index(&mut self) { self.write_u32(self.num_ic_entries.try_into().unwrap()); } fn emit_op(&mut self, opcode: Opcode) { let nuses: isize = opcode.nuses(); assert!(nuses >= 0); self.emit_op_common(opcode, nuses as usize); } fn emit_argc_op(&mut self, opcode: Opcode, argc: u16) { assert!(opcode.has_argc()); assert_eq!(opcode.nuses(), -1); let nuses = match opcode { Opcode::Call | Opcode::CallIgnoresRv | Opcode::Eval | Opcode::CallIter | Opcode::StrictEval => { // callee, this, arguments... 2 + (argc as usize) } Opcode::New | Opcode::SuperCall => { // callee, isConstructing, arguments..., newtarget 2 + (argc as usize) + 1 } _ => panic!("Unsupported opcode"), }; self.emit_op_common(opcode, nuses); } fn emit_pop_n_op(&mut self, opcode: Opcode, n: u16) { assert_eq!(opcode.nuses(), -1); debug_assert_eq!(opcode, Opcode::PopN); self.emit_op_common(opcode, n as usize); } fn emit_op_common(&mut self, opcode: Opcode, nuses: usize) { assert!( self.stack_depth >= nuses as usize, "InstructionWriter misuse! Not enough arguments on the stack." ); self.stack_depth -= nuses as usize; let ndefs = opcode.ndefs(); if ndefs > 0 { self.stack_depth += ndefs; if self.stack_depth > self.maximum_stack_depth { self.maximum_stack_depth = self.stack_depth; } } if opcode.has_ic_entry() { self.num_ic_entries += 1; } self.bytecode.push(opcode.to_byte()); } fn set_last_jump_target_offset(&mut self, target: BytecodeOffset) { self.last_jump_target_offset = Some(target); } fn get_end_of_bytecode(&mut self, offset: BytecodeOffset) -> usize { // find the offset after the end of bytecode associated with this offset. let target_opcode = Opcode::try_from(self.bytecode[offset.offset]).unwrap(); offset.offset + target_opcode.instruction_length() } pub fn emit_jump_target_and_patch(&mut self, jumplist: &Vec) { let mut target = self.bytecode_offset(); let last_jump = self.last_jump_target_offset; match last_jump { Some(offset) => { if self.get_end_of_bytecode(offset) != target.offset { self.jump_target(); self.set_last_jump_target_offset(target); } else { target = offset; } } None => { self.jump_target(); self.set_last_jump_target_offset(target); } } for jump in jumplist { self.patch_jump_to_target(target, *jump); } } pub fn patch_jump_to_target(&mut self, target: BytecodeOffset, jump: BytecodeOffset) { let diff = target.diff_from(jump).into(); let index = jump.offset + 1; // FIXME: Use native endian instead of little endian LittleEndian::write_i32(&mut self.bytecode[index..index + 4], diff); } pub fn bytecode_offset(&mut self) -> BytecodeOffset { BytecodeOffset::from(self.bytecode.len()) } pub fn stack_depth(&self) -> usize { self.stack_depth } pub fn set_stack_depth(&mut self, depth: usize) { self.stack_depth = depth; } // Public methods to emit each instruction. pub fn emit_boolean(&mut self, value: bool) { self.emit_op(if value { Opcode::True } else { Opcode::False }); } pub fn emit_unary_op(&mut self, opcode: Opcode) { assert!(opcode.is_simple_unary_operator()); self.emit_op(opcode); } pub fn emit_binary_op(&mut self, opcode: Opcode) { assert!(opcode.is_simple_binary_operator()); debug_assert_eq!(opcode.nuses(), 2); debug_assert_eq!(opcode.ndefs(), 1); self.emit_op(opcode); } pub fn table_switch( &mut self, _len: i32, _low: i32, _high: i32, _first_resume_index: u24, ) -> Result<(), EmitError> { Err(EmitError::NotImplemented("TODO: table_switch")) } pub fn numeric(&mut self, value: f64) { if value.is_finite() && value.fract() == 0.0 { if i8::min_value() as f64 <= value && value <= i8::max_value() as f64 { match value as i8 { 0 => self.zero(), 1 => self.one(), i => self.int8(i), } return; } if 0.0 <= value { if value <= u16::max_value() as f64 { self.uint16(value as u16); return; } if value <= 0x00ffffff as f64 { self.uint24(value as u24); return; } } if i32::min_value() as f64 <= value && value <= i32::max_value() as f64 { self.int32(value as i32); return; } } self.double_(value); } // WARNING // The following section is generated by update_stencil.py. // Do mot modify manually. // // @@@@ BEGIN METHODS @@@@ pub fn undefined(&mut self) { self.emit_op(Opcode::Undefined); } pub fn null(&mut self) { self.emit_op(Opcode::Null); } pub fn int32(&mut self, val: i32) { self.emit_op(Opcode::Int32); self.write_i32(val); } pub fn zero(&mut self) { self.emit_op(Opcode::Zero); } pub fn one(&mut self) { self.emit_op(Opcode::One); } pub fn int8(&mut self, val: i8) { self.emit_op(Opcode::Int8); self.write_i8(val); } pub fn uint16(&mut self, val: u16) { self.emit_op(Opcode::Uint16); self.write_u16(val); } pub fn uint24(&mut self, val: u24) { self.emit_op(Opcode::Uint24); self.write_u24(val); } pub fn double_(&mut self, val: f64) { self.emit_op(Opcode::Double); self.write_f64(val); } pub fn big_int(&mut self, big_int_index: u32) { self.emit_op(Opcode::BigInt); self.write_u32(big_int_index); } pub fn string(&mut self, atom_index: GCThingIndex) { self.emit_op(Opcode::String); self.write_g_c_thing_index(atom_index); } pub fn symbol(&mut self, symbol: u8) { self.emit_op(Opcode::Symbol); self.write_u8(symbol); } pub fn typeof_(&mut self) { self.emit_op(Opcode::Typeof); } pub fn typeof_expr(&mut self) { self.emit_op(Opcode::TypeofExpr); } pub fn inc(&mut self) { self.emit_op(Opcode::Inc); } pub fn dec(&mut self) { self.emit_op(Opcode::Dec); } pub fn to_property_key(&mut self) { self.emit_op(Opcode::ToPropertyKey); } pub fn to_numeric(&mut self) { self.emit_op(Opcode::ToNumeric); } pub fn to_string(&mut self) { self.emit_op(Opcode::ToString); } pub fn is_null_or_undefined(&mut self) { self.emit_op(Opcode::IsNullOrUndefined); } pub fn global_this(&mut self) { self.emit_op(Opcode::GlobalThis); } pub fn non_syntactic_global_this(&mut self) { self.emit_op(Opcode::NonSyntacticGlobalThis); } pub fn new_target(&mut self) { self.emit_op(Opcode::NewTarget); } pub fn dynamic_import(&mut self) { self.emit_op(Opcode::DynamicImport); } pub fn import_meta(&mut self) { self.emit_op(Opcode::ImportMeta); } pub fn new_init(&mut self) { self.emit_op(Opcode::NewInit); } pub fn new_object(&mut self, shape_index: GCThingIndex) { self.emit_op(Opcode::NewObject); self.write_g_c_thing_index(shape_index); } pub fn object(&mut self, object_index: GCThingIndex) { self.emit_op(Opcode::Object); self.write_g_c_thing_index(object_index); } pub fn obj_with_proto(&mut self) { self.emit_op(Opcode::ObjWithProto); } pub fn init_prop(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::InitProp); self.write_g_c_thing_index(name_index); } pub fn init_hidden_prop(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::InitHiddenProp); self.write_g_c_thing_index(name_index); } pub fn init_locked_prop(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::InitLockedProp); self.write_g_c_thing_index(name_index); } pub fn init_elem(&mut self) { self.emit_op(Opcode::InitElem); } pub fn init_hidden_elem(&mut self) { self.emit_op(Opcode::InitHiddenElem); } pub fn init_locked_elem(&mut self) { self.emit_op(Opcode::InitLockedElem); } pub fn init_prop_getter(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::InitPropGetter); self.write_g_c_thing_index(name_index); } pub fn init_hidden_prop_getter(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::InitHiddenPropGetter); self.write_g_c_thing_index(name_index); } pub fn init_elem_getter(&mut self) { self.emit_op(Opcode::InitElemGetter); } pub fn init_hidden_elem_getter(&mut self) { self.emit_op(Opcode::InitHiddenElemGetter); } pub fn init_prop_setter(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::InitPropSetter); self.write_g_c_thing_index(name_index); } pub fn init_hidden_prop_setter(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::InitHiddenPropSetter); self.write_g_c_thing_index(name_index); } pub fn init_elem_setter(&mut self) { self.emit_op(Opcode::InitElemSetter); } pub fn init_hidden_elem_setter(&mut self) { self.emit_op(Opcode::InitHiddenElemSetter); } pub fn get_prop(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::GetProp); self.write_g_c_thing_index(name_index); } pub fn get_elem(&mut self) { self.emit_op(Opcode::GetElem); } pub fn set_prop(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::SetProp); self.write_g_c_thing_index(name_index); } pub fn strict_set_prop(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::StrictSetProp); self.write_g_c_thing_index(name_index); } pub fn set_elem(&mut self) { self.emit_op(Opcode::SetElem); } pub fn strict_set_elem(&mut self) { self.emit_op(Opcode::StrictSetElem); } pub fn del_prop(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::DelProp); self.write_g_c_thing_index(name_index); } pub fn strict_del_prop(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::StrictDelProp); self.write_g_c_thing_index(name_index); } pub fn del_elem(&mut self) { self.emit_op(Opcode::DelElem); } pub fn strict_del_elem(&mut self) { self.emit_op(Opcode::StrictDelElem); } pub fn has_own(&mut self) { self.emit_op(Opcode::HasOwn); } pub fn check_private_field(&mut self, throw_condition: ThrowCondition, msg_kind: ThrowMsgKind) { self.emit_op(Opcode::CheckPrivateField); self.write_u8(throw_condition as u8); self.write_u8(msg_kind as u8); } pub fn new_private_name(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::NewPrivateName); self.write_g_c_thing_index(name_index); } pub fn super_base(&mut self) { self.emit_op(Opcode::SuperBase); } pub fn get_prop_super(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::GetPropSuper); self.write_g_c_thing_index(name_index); } pub fn get_elem_super(&mut self) { self.emit_op(Opcode::GetElemSuper); } pub fn set_prop_super(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::SetPropSuper); self.write_g_c_thing_index(name_index); } pub fn strict_set_prop_super(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::StrictSetPropSuper); self.write_g_c_thing_index(name_index); } pub fn set_elem_super(&mut self) { self.emit_op(Opcode::SetElemSuper); } pub fn strict_set_elem_super(&mut self) { self.emit_op(Opcode::StrictSetElemSuper); } pub fn iter(&mut self) { self.emit_op(Opcode::Iter); } pub fn more_iter(&mut self) { self.emit_op(Opcode::MoreIter); } pub fn is_no_iter(&mut self) { self.emit_op(Opcode::IsNoIter); } pub fn end_iter(&mut self) { self.emit_op(Opcode::EndIter); } pub fn close_iter(&mut self, kind: CompletionKind) { self.emit_op(Opcode::CloseIter); self.write_u8(kind as u8); } pub fn check_is_obj(&mut self, kind: CheckIsObjectKind) { self.emit_op(Opcode::CheckIsObj); self.write_u8(kind as u8); } pub fn check_obj_coercible(&mut self) { self.emit_op(Opcode::CheckObjCoercible); } pub fn to_async_iter(&mut self) { self.emit_op(Opcode::ToAsyncIter); } pub fn mutate_proto(&mut self) { self.emit_op(Opcode::MutateProto); } pub fn new_array(&mut self, length: u32) { self.emit_op(Opcode::NewArray); self.write_u32(length); } pub fn init_elem_array(&mut self, index: u32) { self.emit_op(Opcode::InitElemArray); self.write_u32(index); } pub fn init_elem_inc(&mut self) { self.emit_op(Opcode::InitElemInc); } pub fn hole(&mut self) { self.emit_op(Opcode::Hole); } pub fn reg_exp(&mut self, regexp_index: GCThingIndex) { self.emit_op(Opcode::RegExp); self.write_g_c_thing_index(regexp_index); } pub fn lambda(&mut self, func_index: GCThingIndex) { self.emit_op(Opcode::Lambda); self.write_g_c_thing_index(func_index); } pub fn set_fun_name(&mut self, prefix_kind: FunctionPrefixKind) { self.emit_op(Opcode::SetFunName); self.write_u8(prefix_kind as u8); } pub fn init_home_object(&mut self) { self.emit_op(Opcode::InitHomeObject); } pub fn check_class_heritage(&mut self) { self.emit_op(Opcode::CheckClassHeritage); } pub fn fun_with_proto(&mut self, func_index: GCThingIndex) { self.emit_op(Opcode::FunWithProto); self.write_g_c_thing_index(func_index); } pub fn builtin_object(&mut self, kind: u8) { self.emit_op(Opcode::BuiltinObject); self.write_u8(kind); } pub fn call(&mut self, argc: u16) { self.emit_argc_op(Opcode::Call, argc); self.write_u16(argc); } pub fn call_content(&mut self, argc: u16) { self.emit_argc_op(Opcode::CallContent, argc); self.write_u16(argc); } pub fn call_iter(&mut self, argc: u16) { self.emit_argc_op(Opcode::CallIter, argc); self.write_u16(argc); } pub fn call_content_iter(&mut self, argc: u16) { self.emit_argc_op(Opcode::CallContentIter, argc); self.write_u16(argc); } pub fn call_ignores_rv(&mut self, argc: u16) { self.emit_argc_op(Opcode::CallIgnoresRv, argc); self.write_u16(argc); } pub fn spread_call(&mut self) { self.emit_op(Opcode::SpreadCall); } pub fn optimize_spread_call(&mut self) { self.emit_op(Opcode::OptimizeSpreadCall); } pub fn eval(&mut self, argc: u16) { self.emit_argc_op(Opcode::Eval, argc); self.write_u16(argc); } pub fn spread_eval(&mut self) { self.emit_op(Opcode::SpreadEval); } pub fn strict_eval(&mut self, argc: u16) { self.emit_argc_op(Opcode::StrictEval, argc); self.write_u16(argc); } pub fn strict_spread_eval(&mut self) { self.emit_op(Opcode::StrictSpreadEval); } pub fn implicit_this(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::ImplicitThis); self.write_g_c_thing_index(name_index); } pub fn call_site_obj(&mut self, object_index: GCThingIndex) { self.emit_op(Opcode::CallSiteObj); self.write_g_c_thing_index(object_index); } pub fn is_constructing(&mut self) { self.emit_op(Opcode::IsConstructing); } pub fn new_(&mut self, argc: u16) { self.emit_argc_op(Opcode::New, argc); self.write_u16(argc); } pub fn new_content(&mut self, argc: u16) { self.emit_argc_op(Opcode::NewContent, argc); self.write_u16(argc); } pub fn super_call(&mut self, argc: u16) { self.emit_argc_op(Opcode::SuperCall, argc); self.write_u16(argc); } pub fn spread_new(&mut self) { self.emit_op(Opcode::SpreadNew); } pub fn spread_super_call(&mut self) { self.emit_op(Opcode::SpreadSuperCall); } pub fn super_fun(&mut self) { self.emit_op(Opcode::SuperFun); } pub fn check_this_reinit(&mut self) { self.emit_op(Opcode::CheckThisReinit); } pub fn generator(&mut self) { self.emit_op(Opcode::Generator); } pub fn initial_yield(&mut self, resume_index: u24) { self.emit_op(Opcode::InitialYield); self.write_u24(resume_index); } pub fn after_yield(&mut self) { self.emit_op(Opcode::AfterYield); self.write_ic_index(); } pub fn final_yield_rval(&mut self) { self.emit_op(Opcode::FinalYieldRval); } pub fn yield_(&mut self, resume_index: u24) { self.emit_op(Opcode::Yield); self.write_u24(resume_index); } pub fn is_gen_closing(&mut self) { self.emit_op(Opcode::IsGenClosing); } pub fn async_await(&mut self) { self.emit_op(Opcode::AsyncAwait); } pub fn async_resolve(&mut self, fulfill_or_reject: AsyncFunctionResolveKind) { self.emit_op(Opcode::AsyncResolve); self.write_u8(fulfill_or_reject as u8); } pub fn await_(&mut self, resume_index: u24) { self.emit_op(Opcode::Await); self.write_u24(resume_index); } pub fn can_skip_await(&mut self) { self.emit_op(Opcode::CanSkipAwait); } pub fn maybe_extract_await_value(&mut self) { self.emit_op(Opcode::MaybeExtractAwaitValue); } pub fn resume_kind(&mut self, resume_kind: GeneratorResumeKind) { self.emit_op(Opcode::ResumeKind); self.write_u8(resume_kind as u8); } pub fn check_resume_kind(&mut self) { self.emit_op(Opcode::CheckResumeKind); } pub fn resume(&mut self) { self.emit_op(Opcode::Resume); } pub fn jump_target(&mut self) { self.emit_op(Opcode::JumpTarget); self.write_ic_index(); } pub fn loop_head(&mut self, depth_hint: u8) { self.emit_op(Opcode::LoopHead); self.write_ic_index(); self.write_u8(depth_hint); } pub fn goto_(&mut self, offset: BytecodeOffsetDiff) { self.emit_op(Opcode::Goto); self.write_bytecode_offset_diff(offset); } pub fn jump_if_false(&mut self, forward_offset: BytecodeOffsetDiff) { self.emit_op(Opcode::JumpIfFalse); self.write_bytecode_offset_diff(forward_offset); } pub fn jump_if_true(&mut self, offset: BytecodeOffsetDiff) { self.emit_op(Opcode::JumpIfTrue); self.write_bytecode_offset_diff(offset); } pub fn and_(&mut self, forward_offset: BytecodeOffsetDiff) { self.emit_op(Opcode::And); self.write_bytecode_offset_diff(forward_offset); } pub fn or_(&mut self, forward_offset: BytecodeOffsetDiff) { self.emit_op(Opcode::Or); self.write_bytecode_offset_diff(forward_offset); } pub fn coalesce(&mut self, forward_offset: BytecodeOffsetDiff) { self.emit_op(Opcode::Coalesce); self.write_bytecode_offset_diff(forward_offset); } pub fn case_(&mut self, forward_offset: BytecodeOffsetDiff) { self.emit_op(Opcode::Case); self.write_bytecode_offset_diff(forward_offset); } pub fn default_(&mut self, forward_offset: BytecodeOffsetDiff) { self.emit_op(Opcode::Default); self.write_bytecode_offset_diff(forward_offset); } pub fn return_(&mut self) { self.emit_op(Opcode::Return); } pub fn get_rval(&mut self) { self.emit_op(Opcode::GetRval); } pub fn set_rval(&mut self) { self.emit_op(Opcode::SetRval); } pub fn ret_rval(&mut self) { self.emit_op(Opcode::RetRval); } pub fn check_return(&mut self) { self.emit_op(Opcode::CheckReturn); } pub fn throw_(&mut self) { self.emit_op(Opcode::Throw); } pub fn throw_msg(&mut self, msg_number: ThrowMsgKind) { self.emit_op(Opcode::ThrowMsg); self.write_u8(msg_number as u8); } pub fn throw_set_const(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::ThrowSetConst); self.write_g_c_thing_index(name_index); } pub fn try_(&mut self) { self.emit_op(Opcode::Try); } pub fn try_destructuring(&mut self) { self.emit_op(Opcode::TryDestructuring); } pub fn exception(&mut self) { self.emit_op(Opcode::Exception); } pub fn finally(&mut self) { self.emit_op(Opcode::Finally); } pub fn uninitialized(&mut self) { self.emit_op(Opcode::Uninitialized); } pub fn init_lexical(&mut self, localno: u24) { self.emit_op(Opcode::InitLexical); self.write_u24(localno); } pub fn init_g_lexical(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::InitGLexical); self.write_g_c_thing_index(name_index); } pub fn init_aliased_lexical(&mut self, hops: u8, slot: u24) { self.emit_op(Opcode::InitAliasedLexical); self.write_u8(hops); self.write_u24(slot); } pub fn check_lexical(&mut self, localno: u24) { self.emit_op(Opcode::CheckLexical); self.write_u24(localno); } pub fn check_aliased_lexical(&mut self, hops: u8, slot: u24) { self.emit_op(Opcode::CheckAliasedLexical); self.write_u8(hops); self.write_u24(slot); } pub fn check_this(&mut self) { self.emit_op(Opcode::CheckThis); } pub fn bind_g_name(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::BindGName); self.write_g_c_thing_index(name_index); } pub fn bind_name(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::BindName); self.write_g_c_thing_index(name_index); } pub fn get_name(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::GetName); self.write_g_c_thing_index(name_index); } pub fn get_g_name(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::GetGName); self.write_g_c_thing_index(name_index); } pub fn get_arg(&mut self, argno: u16) { self.emit_op(Opcode::GetArg); self.write_u16(argno); } pub fn get_local(&mut self, localno: u24) { self.emit_op(Opcode::GetLocal); self.write_u24(localno); } pub fn get_aliased_var(&mut self, hops: u8, slot: u24) { self.emit_op(Opcode::GetAliasedVar); self.write_u8(hops); self.write_u24(slot); } pub fn get_aliased_debug_var(&mut self, hops: u8, slot: u24) { self.emit_op(Opcode::GetAliasedDebugVar); self.write_u8(hops); self.write_u24(slot); } pub fn get_import(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::GetImport); self.write_g_c_thing_index(name_index); } pub fn get_bound_name(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::GetBoundName); self.write_g_c_thing_index(name_index); } pub fn get_intrinsic(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::GetIntrinsic); self.write_g_c_thing_index(name_index); } pub fn callee(&mut self) { self.emit_op(Opcode::Callee); } pub fn env_callee(&mut self, num_hops: u8) { self.emit_op(Opcode::EnvCallee); self.write_u8(num_hops); } pub fn set_name(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::SetName); self.write_g_c_thing_index(name_index); } pub fn strict_set_name(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::StrictSetName); self.write_g_c_thing_index(name_index); } pub fn set_g_name(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::SetGName); self.write_g_c_thing_index(name_index); } pub fn strict_set_g_name(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::StrictSetGName); self.write_g_c_thing_index(name_index); } pub fn set_arg(&mut self, argno: u16) { self.emit_op(Opcode::SetArg); self.write_u16(argno); } pub fn set_local(&mut self, localno: u24) { self.emit_op(Opcode::SetLocal); self.write_u24(localno); } pub fn set_aliased_var(&mut self, hops: u8, slot: u24) { self.emit_op(Opcode::SetAliasedVar); self.write_u8(hops); self.write_u24(slot); } pub fn set_intrinsic(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::SetIntrinsic); self.write_g_c_thing_index(name_index); } pub fn push_lexical_env(&mut self, lexical_scope_index: GCThingIndex) { self.emit_op(Opcode::PushLexicalEnv); self.write_g_c_thing_index(lexical_scope_index); } pub fn pop_lexical_env(&mut self) { self.emit_op(Opcode::PopLexicalEnv); } pub fn debug_leave_lexical_env(&mut self) { self.emit_op(Opcode::DebugLeaveLexicalEnv); } pub fn recreate_lexical_env(&mut self, lexical_scope_index: GCThingIndex) { self.emit_op(Opcode::RecreateLexicalEnv); self.write_g_c_thing_index(lexical_scope_index); } pub fn freshen_lexical_env(&mut self, lexical_scope_index: GCThingIndex) { self.emit_op(Opcode::FreshenLexicalEnv); self.write_g_c_thing_index(lexical_scope_index); } pub fn push_class_body_env(&mut self, lexical_scope_index: GCThingIndex) { self.emit_op(Opcode::PushClassBodyEnv); self.write_g_c_thing_index(lexical_scope_index); } pub fn push_var_env(&mut self, scope_index: GCThingIndex) { self.emit_op(Opcode::PushVarEnv); self.write_g_c_thing_index(scope_index); } pub fn enter_with(&mut self, static_with_index: GCThingIndex) { self.emit_op(Opcode::EnterWith); self.write_g_c_thing_index(static_with_index); } pub fn leave_with(&mut self) { self.emit_op(Opcode::LeaveWith); } pub fn bind_var(&mut self) { self.emit_op(Opcode::BindVar); } pub fn global_or_eval_decl_instantiation(&mut self, last_fun: u32) { self.emit_op(Opcode::GlobalOrEvalDeclInstantiation); self.write_u32(last_fun); } pub fn del_name(&mut self, name_index: GCThingIndex) { self.emit_op(Opcode::DelName); self.write_g_c_thing_index(name_index); } pub fn arguments(&mut self) { self.emit_op(Opcode::Arguments); } pub fn rest(&mut self) { self.emit_op(Opcode::Rest); } pub fn function_this(&mut self) { self.emit_op(Opcode::FunctionThis); } pub fn pop(&mut self) { self.emit_op(Opcode::Pop); } pub fn pop_n(&mut self, n: u16) { self.emit_pop_n_op(Opcode::PopN, n); self.write_u16(n); } pub fn dup(&mut self) { self.emit_op(Opcode::Dup); } pub fn dup2(&mut self) { self.emit_op(Opcode::Dup2); } pub fn dup_at(&mut self, n: u24) { self.emit_op(Opcode::DupAt); self.write_u24(n); } pub fn swap(&mut self) { self.emit_op(Opcode::Swap); } pub fn pick(&mut self, n: u8) { self.emit_op(Opcode::Pick); self.write_u8(n); } pub fn unpick(&mut self, n: u8) { self.emit_op(Opcode::Unpick); self.write_u8(n); } pub fn nop(&mut self) { self.emit_op(Opcode::Nop); } pub fn lineno(&mut self, lineno: u32) { self.emit_op(Opcode::Lineno); self.write_u32(lineno); } pub fn nop_destructuring(&mut self) { self.emit_op(Opcode::NopDestructuring); } pub fn force_interpreter(&mut self) { self.emit_op(Opcode::ForceInterpreter); } pub fn debug_check_self_hosted(&mut self) { self.emit_op(Opcode::DebugCheckSelfHosted); } pub fn debugger(&mut self) { self.emit_op(Opcode::Debugger); } // @@@@ END METHODS @@@@ pub fn get_atom_gcthing_index(&mut self, atom: SourceAtomSetIndex) -> GCThingIndex { match self.atom_to_gcindex_map.get(&atom) { Some(index) => *index, None => { let index = self.gcthings.push_atom(atom); self.atom_to_gcindex_map.insert(atom, index); index } } } pub fn get_function_gcthing_index(&mut self, fun_index: ScriptStencilIndex) -> GCThingIndex { self.gcthings.push_function(fun_index) } pub fn get_regexp_gcthing_index(&mut self, regexp_index: RegExpIndex) -> GCThingIndex { self.gcthings.push_regexp(regexp_index) } fn update_max_frame_slots(&mut self, max_frame_slots: FrameSlot) { self.max_fixed_slots = cmp::max(self.max_fixed_slots, max_frame_slots); } pub fn enter_global_scope(&mut self, scope_index: ScopeIndex) { let index = self.gcthings.push_scope(scope_index); self.body_scope_index = Some(index); } pub fn leave_global_scope(&self) {} pub fn enter_lexical_scope( &mut self, scope_index: ScopeIndex, parent_scope_note_index: Option, next_frame_slot: FrameSlot, needs_environment_object: bool, ) -> ScopeNoteIndex { self.update_max_frame_slots(next_frame_slot); let gcthing_index = self.gcthings.push_scope(scope_index); let offset = self.bytecode_offset(); let note_index = self.scope_notes .enter_scope(gcthing_index, offset, parent_scope_note_index); if needs_environment_object { self.push_lexical_env(gcthing_index); } note_index } pub fn leave_lexical_scope(&mut self, index: ScopeNoteIndex, needs_environment_object: bool) { self.emit_leave_lexical_scope(needs_environment_object); let offset = self.bytecode_offset(); self.scope_notes.leave_scope(index, offset); } fn emit_leave_lexical_scope(&mut self, needs_environment_object: bool) { if needs_environment_object { self.pop_lexical_env(); } else { self.debug_leave_lexical_env(); } } pub fn enter_scope_hole_from_lexical( &mut self, maybe_hole_scope_note_index: &Option, parent_scope_note_index: Option, needs_environment_object: bool, ) -> ScopeNoteIndex { self.emit_leave_lexical_scope(needs_environment_object); self.enter_scope_hole(maybe_hole_scope_note_index, parent_scope_note_index) } fn enter_scope_hole( &mut self, maybe_hole_scope_note_index: &Option, parent_scope_note_index: Option, ) -> ScopeNoteIndex { let offset = self.bytecode_offset(); let gcthing_index = match maybe_hole_scope_note_index { Some(index) => self.scope_notes.get_scope_hole_gcthing_index(index), None => self .body_scope_index .expect("we should have a body scope index"), }; self.scope_notes .enter_scope(gcthing_index, offset, parent_scope_note_index) } pub fn leave_scope_hole(&mut self, index: ScopeNoteIndex) { let offset = self.bytecode_offset(); self.scope_notes.leave_scope(index, offset); } pub fn switch_to_main(&mut self) { self.main_offset = self.bytecode_offset(); } pub fn into_stencil( self, script_data_list: &mut ImmutableScriptDataList, extent: SourceExtent, ) -> Result { let main_offset: usize = self.main_offset.into(); let nfixed: u32 = self.max_fixed_slots.into(); let nslots = nfixed as usize + self.maximum_stack_depth; let immutable_script_data = script_data_list.push(ImmutableScriptData { main_offset: main_offset .try_into() .map_err(|_| EmitError::NotImplemented("Throwing allocation overflow"))?, nfixed: self.max_fixed_slots, nslots: nslots .try_into() .map_err(|_| EmitError::NotImplemented("Throwing JSMSG_NEED_DIET"))?, body_scope_index: usize::from(self.body_scope_index.expect("body scope should be set")) .try_into() .map_err(|_| EmitError::NotImplemented("Throwing allocation overflow"))?, num_ic_entries: self.num_ic_entries.try_into().unwrap(), fun_length: 0, bytecode: self.bytecode, scope_notes: self.scope_notes.into(), }); Ok(ScriptStencil::top_level_script( self.gcthings.into(), immutable_script_data, extent, )) } }