Skip to main content

boa_engine/vm/
code_block.rs

1//! `CodeBlock`
2//!
3//! This module is for the `CodeBlock` which implements a function representation in the VM
4
5use crate::{
6    Context, JsBigInt, JsString, JsValue, SpannedSourceText,
7    builtins::{
8        OrdinaryObject,
9        function::{OrdinaryFunction, ThisMode},
10    },
11    object::JsObject,
12};
13use bitflags::bitflags;
14use boa_ast::scope::{BindingLocator, Scope};
15use boa_gc::{Finalize, Gc, Trace, empty_trace};
16use itertools::Itertools;
17use std::{cell::Cell, fmt::Display, fmt::Write as _};
18use thin_vec::ThinVec;
19
20use super::{
21    InlineCache,
22    opcode::{Address, Bytecode, Instruction, InstructionIterator},
23    source_info::{SourceInfo, SourceMap, SourcePath},
24};
25
26bitflags! {
27    /// Flags for [`CodeBlock`].
28    #[derive(Clone, Copy, Debug, Finalize)]
29    pub(crate) struct CodeBlockFlags: u16 {
30        /// Is this function in strict mode.
31        const STRICT = 0b0000_0001;
32
33        /// Indicates if the function is an expression and has a binding identifier.
34        const HAS_BINDING_IDENTIFIER = 0b0000_0010;
35
36        /// The `[[IsClassConstructor]]` internal slot.
37        const IS_CLASS_CONSTRUCTOR = 0b0000_0100;
38
39        /// The `[[ClassFieldInitializerName]]` internal slot.
40        const IN_CLASS_FIELD_INITIALIZER = 0b0000_1000;
41
42        /// `[[ConstructorKind]]`
43        const IS_DERIVED_CONSTRUCTOR = 0b0001_0000;
44
45        const IS_ASYNC = 0b0010_0000;
46        const IS_GENERATOR = 0b0100_0000;
47
48        /// Arrow and method functions don't have `"prototype"` property.
49        const HAS_PROTOTYPE_PROPERTY = 0b1000_0000;
50
51        /// If the function requires a function scope.
52        const HAS_FUNCTION_SCOPE = 0b1_0000_0000;
53
54        /// Trace instruction execution to `stdout`.
55        #[cfg(feature = "trace")]
56        const TRACEABLE = 0b1000_0000_0000_0000;
57    }
58}
59
60impl CodeBlockFlags {
61    /// Check if the [`CodeBlock`] has a function scope.
62    #[must_use]
63    pub(crate) fn has_function_scope(self) -> bool {
64        self.contains(Self::HAS_FUNCTION_SCOPE)
65    }
66}
67
68// SAFETY: Nothing in CodeBlockFlags needs tracing, so this is safe.
69unsafe impl Trace for CodeBlockFlags {
70    empty_trace!();
71}
72
73/// This represents a range in the code that handles exception throws.
74///
75/// When a throw happens, we search for handler in the [`CodeBlock`] using
76/// the [`CodeBlock::find_handler()`] method.
77///
78/// If any exception happens and gets caught by this handler, the `pc` will be set to `end` of the
79/// [`Handler`] and remove any environments or stack values that where pushed after the handler.
80#[derive(Debug, Clone, Copy)]
81pub(crate) struct Handler {
82    pub(crate) start: Address,
83    pub(crate) end: Address,
84    pub(crate) environment_count: u32,
85}
86
87impl Handler {
88    /// Get the handler address.
89    pub(crate) const fn handler(&self) -> Address {
90        self.end
91    }
92
93    /// Check if the provided `pc` is contained in the handler range.
94    pub(crate) const fn contains(&self, pc: u32) -> bool {
95        pc < self.end.as_u32() && pc >= self.start.as_u32()
96    }
97}
98
99#[derive(Clone, Debug, Trace, Finalize)]
100pub(crate) enum Constant {
101    /// Property field names and private names `[[description]]`s.
102    String(JsString),
103    Function(Gc<CodeBlock>),
104    BigInt(#[unsafe_ignore_trace] JsBigInt),
105
106    /// Declarative or function scope.
107    // Safety: Nothing in `Scope` needs tracing, so this is safe.
108    Scope(#[unsafe_ignore_trace] Scope),
109}
110
111/// Binding information about a global function.
112#[derive(Copy, Clone, Debug, Trace, Finalize)]
113#[boa_gc(empty_trace)]
114pub(crate) struct GlobalFunctionBinding {
115    /// The index of the global function's name in the constants array.
116    pub(crate) name_index: u32,
117    /// The index of the global function in the constants array
118    pub(crate) function_index: u32,
119}
120
121/// The internal representation of a JavaScript function.
122///
123/// A `CodeBlock` is generated for each function compiled by the
124/// [`ByteCompiler`](crate::bytecompiler::ByteCompiler). It stores the bytecode and the other
125/// attributes of the function.
126#[derive(Clone, Debug, Trace, Finalize)]
127pub struct CodeBlock {
128    #[unsafe_ignore_trace]
129    pub(crate) flags: Cell<CodeBlockFlags>,
130
131    /// The number of arguments expected.
132    pub(crate) length: u32,
133
134    pub(crate) parameter_length: u32,
135
136    pub(crate) register_count: u32,
137
138    /// `[[ThisMode]]`
139    pub(crate) this_mode: ThisMode,
140
141    /// Used for constructing a `MappedArguments` object.
142    #[unsafe_ignore_trace]
143    pub(crate) mapped_arguments_binding_indices: ThinVec<Option<u32>>,
144
145    /// Bytecode
146    #[unsafe_ignore_trace]
147    pub(crate) bytecode: Bytecode,
148
149    pub(crate) constants: ThinVec<Constant>,
150
151    /// Locators for all bindings in the codeblock.
152    #[unsafe_ignore_trace]
153    pub(crate) bindings: Box<[BindingLocator]>,
154
155    /// Exception [`Handler`]s.
156    #[unsafe_ignore_trace]
157    pub(crate) handlers: ThinVec<Handler>,
158
159    /// inline caching
160    pub(crate) ic: Box<[InlineCache]>,
161
162    /// Bytecode to source code mapping.
163    pub(crate) source_info: SourceInfo,
164
165    pub(crate) global_lexs: Box<[u32]>,
166    pub(crate) global_fns: Box<[GlobalFunctionBinding]>,
167    pub(crate) global_vars: Box<[u32]>,
168
169    // Used for identifying anonymous functions in compiled output and call frames.
170    pub(crate) debug_id: u64,
171
172    #[cfg(feature = "trace")]
173    #[unsafe_ignore_trace]
174    pub(crate) traced: Cell<bool>,
175
176    #[cfg(feature = "jit")]
177    #[unsafe_ignore_trace]
178    pub(crate) jit: crate::vm::jit::JitSlot,
179}
180
181/// ---- `CodeBlock` public API ----
182impl CodeBlock {
183    /// Creates a new `CodeBlock`.
184    #[must_use]
185    pub fn new(name: JsString, length: u32, strict: bool) -> Self {
186        let mut flags = CodeBlockFlags::empty();
187        flags.set(CodeBlockFlags::STRICT, strict);
188        Self {
189            bytecode: Bytecode::default(),
190            constants: ThinVec::default(),
191            bindings: Box::default(),
192            flags: Cell::new(flags),
193            length,
194            register_count: 0,
195            this_mode: ThisMode::Global,
196            mapped_arguments_binding_indices: ThinVec::new(),
197            parameter_length: 0,
198            handlers: ThinVec::default(),
199            ic: Box::default(),
200            source_info: SourceInfo::new(
201                SourceMap::new(Box::default(), SourcePath::None),
202                name,
203                SpannedSourceText::new_empty(),
204            ),
205            global_lexs: Box::default(),
206            global_fns: Box::default(),
207            global_vars: Box::default(),
208            debug_id: CodeBlock::get_next_codeblock_id(),
209            #[cfg(feature = "trace")]
210            traced: Cell::new(false),
211            #[cfg(feature = "jit")]
212            jit: crate::vm::jit::JitSlot::default(),
213        }
214    }
215
216    /// Retrieves the name associated with this code block.
217    #[must_use]
218    pub fn name(&self) -> &JsString {
219        self.source_info.function_name()
220    }
221
222    /// Retrieves the path of this code block.
223    #[must_use]
224    pub fn path(&self) -> &SourcePath {
225        self.source_info.map().path()
226    }
227
228    /// Check if the function is traced.
229    #[cfg(feature = "trace")]
230    pub(crate) fn traceable(&self) -> bool {
231        self.flags.get().contains(CodeBlockFlags::TRACEABLE)
232    }
233    /// Enable or disable instruction tracing to `stdout`.
234    #[cfg(feature = "trace")]
235    #[inline]
236    pub fn set_traceable(&self, value: bool) {
237        let mut flags = self.flags.get();
238        flags.set(CodeBlockFlags::TRACEABLE, value);
239        self.flags.set(flags);
240    }
241
242    /// Check if the function is a class constructor.
243    pub(crate) fn is_class_constructor(&self) -> bool {
244        self.flags
245            .get()
246            .contains(CodeBlockFlags::IS_CLASS_CONSTRUCTOR)
247    }
248
249    /// Check if the function is in strict mode.
250    pub(crate) fn strict(&self) -> bool {
251        self.flags.get().contains(CodeBlockFlags::STRICT)
252    }
253
254    /// Indicates if the function is an expression and has a binding identifier.
255    pub(crate) fn has_binding_identifier(&self) -> bool {
256        self.flags
257            .get()
258            .contains(CodeBlockFlags::HAS_BINDING_IDENTIFIER)
259    }
260
261    /// Does this function have the `[[ClassFieldInitializerName]]` internal slot set to non-empty value.
262    pub(crate) fn in_class_field_initializer(&self) -> bool {
263        self.flags
264            .get()
265            .contains(CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER)
266    }
267
268    /// Returns true if this function is a derived constructor.
269    pub(crate) fn is_derived_constructor(&self) -> bool {
270        self.flags
271            .get()
272            .contains(CodeBlockFlags::IS_DERIVED_CONSTRUCTOR)
273    }
274
275    /// Returns true if this function an async function.
276    pub(crate) fn is_async(&self) -> bool {
277        self.flags.get().contains(CodeBlockFlags::IS_ASYNC)
278    }
279
280    /// Returns true if this function an generator function.
281    pub(crate) fn is_generator(&self) -> bool {
282        self.flags.get().contains(CodeBlockFlags::IS_GENERATOR)
283    }
284
285    /// Returns true if this function a async generator function.
286    pub(crate) fn is_async_generator(&self) -> bool {
287        self.flags
288            .get()
289            .contains(CodeBlockFlags::IS_ASYNC | CodeBlockFlags::IS_GENERATOR)
290    }
291
292    /// Returns true if this function an async function.
293    pub(crate) fn is_ordinary(&self) -> bool {
294        !self.is_async() && !self.is_generator()
295    }
296
297    /// Returns true if this function has the `"prototype"` property when function object is created.
298    pub(crate) fn has_prototype_property(&self) -> bool {
299        self.flags
300            .get()
301            .contains(CodeBlockFlags::HAS_PROTOTYPE_PROPERTY)
302    }
303
304    /// Returns true if this function requires a function scope.
305    pub(crate) fn has_function_scope(&self) -> bool {
306        self.flags.get().has_function_scope()
307    }
308
309    /// Find exception [`Handler`] in the code block given the current program counter (`pc`).
310    #[inline]
311    pub(crate) fn find_handler(&self, pc: u32) -> Option<(usize, &Handler)> {
312        self.handlers
313            .iter()
314            .enumerate()
315            .rev()
316            .find(|(_, handler)| handler.contains(pc))
317    }
318
319    /// Get the [`JsString`] constant from the [`CodeBlock`].
320    ///
321    /// # Panics
322    ///
323    /// If the type of the [`Constant`] is not [`Constant::String`].
324    /// Or `index` is greater or equal to length of `constants`.
325    pub(crate) fn constant_string(&self, index: usize) -> JsString {
326        if let Some(Constant::String(value)) = self.constants.get(index) {
327            return value.clone();
328        }
329
330        panic!("expected string constant at index {index}")
331    }
332
333    /// Get the function ([`Gc<CodeBlock>`]) constant from the [`CodeBlock`].
334    ///
335    /// # Panics
336    ///
337    /// If the type of the [`Constant`] is not [`Constant::Function`].
338    /// Or `index` is greater or equal to length of `constants`.
339    pub(crate) fn constant_function(&self, index: usize) -> Gc<Self> {
340        if let Some(Constant::Function(value)) = self.constants.get(index) {
341            return value.clone();
342        }
343
344        panic!("expected function constant at index {index}")
345    }
346
347    /// Get the [`Scope`] constant from the [`CodeBlock`].
348    ///
349    /// # Panics
350    ///
351    /// If the type of the [`Constant`] is not [`Constant::Scope`].
352    /// Or `index` is greater or equal to length of `constants`.
353    pub(crate) fn constant_scope(&self, index: usize) -> Scope {
354        if let Some(Constant::Scope(value)) = self.constants.get(index) {
355            return value.clone();
356        }
357
358        panic!("expected scope constant at index {index}")
359    }
360
361    pub(crate) fn source_info(&self) -> &SourceInfo {
362        &self.source_info
363    }
364
365    pub(crate) fn get_next_codeblock_id() -> u64 {
366        thread_local! {
367            static CODEBLOCK_ID_COUNTER: Cell<u64> = const { Cell::new(0) };
368        }
369
370        CODEBLOCK_ID_COUNTER.with(|c| {
371            let id = c.get();
372            c.set(id + 1);
373            id
374        })
375    }
376}
377
378/// ---- `CodeBlock` private API ----
379impl CodeBlock {
380    /// Get the operands after the `Opcode` pointed to by `pc` as a `String`.
381    /// Modifies the `pc` to point to the next instruction.
382    ///
383    /// Returns an empty `String` if no operands are present.
384    pub(crate) fn instruction_operands(&self, instruction: &Instruction) -> String {
385        match instruction {
386            Instruction::SetRegisterFromAccumulator { dst }
387            | Instruction::PopIntoRegister { dst }
388            | Instruction::StoreZero { dst }
389            | Instruction::StoreOne { dst }
390            | Instruction::StoreNan { dst }
391            | Instruction::StorePositiveInfinity { dst }
392            | Instruction::StoreNegativeInfinity { dst }
393            | Instruction::StoreNull { dst }
394            | Instruction::StoreTrue { dst }
395            | Instruction::StoreFalse { dst }
396            | Instruction::StoreUndefined { dst }
397            | Instruction::Exception { dst }
398            | Instruction::This { dst }
399            | Instruction::NewTarget { dst }
400            | Instruction::ImportMeta { dst }
401            | Instruction::CreateMappedArgumentsObject { dst }
402            | Instruction::CreateUnmappedArgumentsObject { dst }
403            | Instruction::RestParameterInit { dst }
404            | Instruction::StoreNewArray { dst } => format!("dst:{dst}"),
405            Instruction::Add { lhs, rhs, dst }
406            | Instruction::Sub { lhs, rhs, dst }
407            | Instruction::Div { lhs, rhs, dst }
408            | Instruction::Mul { lhs, rhs, dst }
409            | Instruction::Mod { lhs, rhs, dst }
410            | Instruction::Pow { lhs, rhs, dst }
411            | Instruction::ShiftRight { lhs, rhs, dst }
412            | Instruction::ShiftLeft { lhs, rhs, dst }
413            | Instruction::UnsignedShiftRight { lhs, rhs, dst }
414            | Instruction::BitOr { lhs, rhs, dst }
415            | Instruction::BitAnd { lhs, rhs, dst }
416            | Instruction::BitXor { lhs, rhs, dst }
417            | Instruction::In { lhs, rhs, dst }
418            | Instruction::Eq { lhs, rhs, dst }
419            | Instruction::StrictEq { lhs, rhs, dst }
420            | Instruction::NotEq { lhs, rhs, dst }
421            | Instruction::StrictNotEq { lhs, rhs, dst }
422            | Instruction::GreaterThan { lhs, rhs, dst }
423            | Instruction::GreaterThanOrEq { lhs, rhs, dst }
424            | Instruction::LessThan { lhs, rhs, dst }
425            | Instruction::LessThanOrEq { lhs, rhs, dst }
426            | Instruction::InstanceOf { lhs, rhs, dst } => {
427                format!("lhs:{lhs}, rhs:{rhs}, dst:{dst}")
428            }
429            Instruction::InPrivate { dst, index, rhs } => {
430                format!("rhs:{rhs}, index:{index}, dst:{dst}")
431            }
432            Instruction::Inc { src, dst }
433            | Instruction::Dec { src, dst }
434            | Instruction::Move { src, dst }
435            | Instruction::ToPropertyKey { src, dst } => {
436                format!("src:{src}, dst:{dst}")
437            }
438            Instruction::SetFunctionName {
439                function,
440                name,
441                prefix,
442            } => {
443                format!(
444                    "function:{function}, name:{name}, prefix:{}",
445                    match u32::from(*prefix) {
446                        0 => "prefix:",
447                        1 => "prefix: get",
448                        2 => "prefix: set",
449                        _ => unreachable!(),
450                    }
451                )
452            }
453            Instruction::StoreInt8 { value, dst } => {
454                format!("value:{value}, dst:{dst}")
455            }
456            Instruction::StoreInt16 { value, dst } => {
457                format!("value:{value}, dst:{dst}")
458            }
459            Instruction::StoreInt32 { value, dst } => {
460                format!("value:{value}, dst:{dst}")
461            }
462            Instruction::StoreFloat { value, dst } => {
463                format!("value:{value}, dst:{dst}")
464            }
465            Instruction::StoreDouble { value, dst } => {
466                format!("value:{value}, dst:{dst}")
467            }
468            Instruction::StoreLiteral { index, dst }
469            | Instruction::ThisForObjectEnvironmentName { index, dst }
470            | Instruction::GetFunction { index, dst }
471            | Instruction::GetArgument { index, dst } => {
472                format!("index:{index}, dst:{dst}")
473            }
474            Instruction::ThrowNewTypeError { message }
475            | Instruction::ThrowNewReferenceError { message } => format!("message:{message}"),
476            Instruction::StoreRegexp {
477                pattern_index,
478                flags_index,
479                dst,
480            } => {
481                format!("pattern:{pattern_index}, flags:{flags_index}, dst:{dst}")
482            }
483            Instruction::Jump { address } => format!("address:{address}"),
484            Instruction::JumpIfTrue { address, value }
485            | Instruction::JumpIfFalse { address, value }
486            | Instruction::JumpIfNotUndefined { address, value }
487            | Instruction::JumpIfNullOrUndefined { address, value }
488            | Instruction::LogicalAnd { address, value }
489            | Instruction::LogicalOr { address, value }
490            | Instruction::Coalesce { address, value } => {
491                format!("value:{value}, address:{address}")
492            }
493            Instruction::JumpIfNotLessThan { address, lhs, rhs }
494            | Instruction::JumpIfNotLessThanOrEqual { address, lhs, rhs }
495            | Instruction::JumpIfNotGreaterThan { address, lhs, rhs }
496            | Instruction::JumpIfNotGreaterThanOrEqual { address, lhs, rhs }
497            | Instruction::JumpIfNotEqual { address, lhs, rhs } => {
498                format!("lhs:{lhs}, rhs:{rhs}, address:{address}")
499            }
500            Instruction::Case {
501                address,
502                value,
503                condition,
504            } => {
505                format!("value:{value}, condition:{condition}, address:{address}")
506            }
507            Instruction::CallEval {
508                argument_count,
509                scope_index,
510            } => {
511                format!("argument_count:{argument_count}, scope_index:{scope_index}")
512            }
513            Instruction::CallEvalSpread { scope_index }
514            | Instruction::PushScope { scope_index } => {
515                format!("scope_index:{scope_index}")
516            }
517            Instruction::Call { argument_count }
518            | Instruction::New { argument_count }
519            | Instruction::SuperCall { argument_count } => {
520                format!("argument_count:{argument_count}")
521            }
522            Instruction::DefVar { binding_index }
523            | Instruction::DefEvalVar { binding_index }
524            | Instruction::GetLocator { binding_index } => format!("binding_index:{binding_index}"),
525            Instruction::DefInitVar { src, binding_index }
526            | Instruction::PutLexicalValue { src, binding_index }
527            | Instruction::SetName { src, binding_index } => {
528                format!("src:{src}, binding_index:{binding_index}")
529            }
530            Instruction::GetName { dst, binding_index }
531            | Instruction::GetNameAndLocator { dst, binding_index }
532            | Instruction::GetNameOrUndefined { dst, binding_index }
533            | Instruction::DeleteName { dst, binding_index } => {
534                format!("dst:{dst}, binding_index:{binding_index}")
535            }
536            Instruction::GetNameGlobal {
537                dst,
538                binding_index,
539                ic_index,
540            } => {
541                format!("dst:{dst}, binding_index:{binding_index}, ic_index:{ic_index}")
542            }
543            Instruction::DefineOwnPropertyByName {
544                object,
545                value,
546                name_index,
547            }
548            | Instruction::SetPropertyGetterByName {
549                object,
550                value,
551                name_index,
552            }
553            | Instruction::SetPropertySetterByName {
554                object,
555                value,
556                name_index,
557            }
558            | Instruction::DefinePrivateField {
559                object,
560                value,
561                name_index,
562            }
563            | Instruction::SetPrivateMethod {
564                object,
565                value,
566                name_index,
567            }
568            | Instruction::SetPrivateSetter {
569                object,
570                value,
571                name_index,
572            }
573            | Instruction::SetPrivateGetter {
574                object,
575                value,
576                name_index,
577            }
578            | Instruction::PushClassPrivateGetter {
579                object,
580                value,
581                name_index,
582            }
583            | Instruction::PushClassPrivateSetter {
584                object,
585                value,
586                name_index,
587            }
588            | Instruction::DefineClassStaticMethodByName {
589                object,
590                value,
591                name_index,
592            }
593            | Instruction::DefineClassMethodByName {
594                object,
595                value,
596                name_index,
597            }
598            | Instruction::DefineClassStaticGetterByName {
599                object,
600                value,
601                name_index,
602            }
603            | Instruction::DefineClassGetterByName {
604                object,
605                value,
606                name_index,
607            }
608            | Instruction::DefineClassStaticSetterByName {
609                object,
610                value,
611                name_index,
612            }
613            | Instruction::DefineClassSetterByName {
614                object,
615                value,
616                name_index,
617            }
618            | Instruction::SetPrivateField {
619                object,
620                value,
621                name_index,
622            }
623            | Instruction::PushClassFieldPrivate {
624                object,
625                value,
626                name_index,
627            } => {
628                format!("object:{object}, value:{value}, name_index:{name_index}")
629            }
630            Instruction::GetPrivateField {
631                dst,
632                object,
633                name_index,
634            } => {
635                format!("dst:{dst}, object:{object}, name_index:{name_index}")
636            }
637            Instruction::PushClassPrivateMethod {
638                object,
639                proto,
640                value,
641                name_index,
642            } => {
643                format!("object:{object}, proto:{proto}, value:{value}, name_index:{name_index}")
644            }
645            Instruction::ThrowMutateImmutable { index } => {
646                format!("index:{index}")
647            }
648            Instruction::DeletePropertyByName { object, name_index }
649            | Instruction::GetMethod { object, name_index } => {
650                format!("object:{object}, name_index:{name_index}")
651            }
652            Instruction::GetLengthProperty {
653                dst,
654                value,
655                ic_index,
656            }
657            | Instruction::GetPropertyByName {
658                dst,
659                value,
660                ic_index,
661            } => {
662                let ic = &self.ic[u32::from(*ic_index) as usize];
663                format!("dst:{dst}, value:{value}, ic:{ic}")
664            }
665            Instruction::GetPropertyByNameWithThis {
666                dst,
667                receiver,
668                value,
669                ic_index,
670            } => {
671                let ic = &self.ic[u32::from(*ic_index) as usize];
672                format!("dst:{dst}, receiver:{receiver}, value:{value}, ic:{ic}")
673            }
674            Instruction::SetPropertyByName {
675                value,
676                object,
677                ic_index,
678            } => {
679                let ic = &self.ic[u32::from(*ic_index) as usize];
680                format!("object:{object}, value:{value}, ic:{ic}")
681            }
682            Instruction::SetPropertyByNameWithThis {
683                value,
684                receiver,
685                object,
686                ic_index,
687            } => {
688                let ic = &self.ic[u32::from(*ic_index) as usize];
689                format!("object:{object}, receiver:{receiver}, value:{value}, ic:{ic}")
690            }
691            Instruction::GetPropertyByIndex { dst, object, index } => {
692                format!("dst:{dst}, object:{object}, index:{index}")
693            }
694            Instruction::SetPropertyByIndex {
695                value,
696                object,
697                index,
698            } => {
699                format!("value:{value}, object:{object}, index:{index}")
700            }
701            Instruction::GetPropertyByValue {
702                dst,
703                key,
704                receiver,
705                object,
706            }
707            | Instruction::GetPropertyByValuePush {
708                dst,
709                key,
710                receiver,
711                object,
712            } => {
713                format!("dst:{dst}, object:{object}, receiver:{receiver}, key:{key}")
714            }
715            Instruction::SetPropertyByValue {
716                value,
717                key,
718                receiver,
719                object,
720            } => {
721                format!("object:{object}, receiver:{receiver}, key:{key}, value:{value}")
722            }
723            Instruction::DefineOwnPropertyByValue { value, key, object }
724            | Instruction::DefineClassStaticMethodByValue { value, key, object }
725            | Instruction::DefineClassMethodByValue { value, key, object }
726            | Instruction::SetPropertyGetterByValue { value, key, object }
727            | Instruction::DefineClassStaticGetterByValue { value, key, object }
728            | Instruction::DefineClassGetterByValue { value, key, object }
729            | Instruction::SetPropertySetterByValue { value, key, object }
730            | Instruction::DefineClassStaticSetterByValue { value, key, object }
731            | Instruction::DefineClassSetterByValue { value, key, object } => {
732                format!("object:{object}, key:{key}, value:{value}")
733            }
734            Instruction::DeletePropertyByValue { key, object } => {
735                format!("object:{object}, key:{key}")
736            }
737            Instruction::CreateIteratorResult { value, done } => {
738                format!("value:{value}, done:{done}")
739            }
740            Instruction::StoreClassPrototype {
741                dst,
742                class,
743                superclass,
744            } => {
745                format!("dst:{dst}, class:{class}, superclass:{superclass}")
746            }
747            Instruction::SetClassPrototype {
748                dst,
749                prototype,
750                class,
751            } => {
752                format!("dst:{dst}, prototype:{prototype}, class:{class}")
753            }
754            Instruction::SetHomeObject { function, home } => {
755                format!("function:{function}, home:{home}")
756            }
757            Instruction::GetHomeObject { function } => {
758                format!("function:{function}")
759            }
760            Instruction::SetPrototype { object, prototype } => {
761                format!("object:{object}, prototype:{prototype}")
762            }
763            Instruction::GetPrototype { object } => {
764                format!("object:{object}")
765            }
766            Instruction::PushValueToArray { value, array } => {
767                format!("value:{value}, array:{array}")
768            }
769            Instruction::PushElisionToArray { array }
770            | Instruction::PushIteratorToArray { array } => {
771                format!("array:{array}")
772            }
773            Instruction::TypeOf { value }
774            | Instruction::LogicalNot { value }
775            | Instruction::Pos { value }
776            | Instruction::Neg { value }
777            | Instruction::IsObject { value }
778            | Instruction::BindThisValue { value }
779            | Instruction::BitNot { value } => {
780                format!("value:{value}")
781            }
782            Instruction::ImportCall {
783                specifier,
784                options,
785                phase,
786            } => {
787                let phase_str = match u32::from(*phase) {
788                    0 => "evaluation",
789                    1 => "defer",
790                    2 => "source",
791                    _ => "unknown",
792                };
793                format!("specifier:{specifier}, options:{options}, phase:{phase_str}")
794            }
795            Instruction::PushClassField {
796                object,
797                name,
798                value,
799                is_anonymous_function,
800            } => {
801                format!(
802                    "object:{object}, value:{value}, name:{name}, is_anonymous_function:{is_anonymous_function}"
803                )
804            }
805            Instruction::MaybeException {
806                has_exception,
807                exception,
808            } => {
809                format!("has_exception:{has_exception}, exception:{exception}")
810            }
811            Instruction::SetAccumulator { src }
812            | Instruction::PushFromRegister { src }
813            | Instruction::Throw { src }
814            | Instruction::SetNameByLocator { src }
815            | Instruction::PushObjectEnvironment { src }
816            | Instruction::CreateForInIterator { src }
817            | Instruction::GetIterator { src }
818            | Instruction::GetAsyncIterator { src }
819            | Instruction::ValueNotNullOrUndefined { src }
820            | Instruction::GeneratorYield { src }
821            | Instruction::AsyncGeneratorYield { src }
822            | Instruction::Await { src } => {
823                format!("src:{src}")
824            }
825            Instruction::IteratorPush { iterator, next }
826            | Instruction::IteratorPop { iterator, next } => {
827                format!("iterator:{iterator}, next:{next}")
828            }
829            Instruction::IteratorUpdateResult { result } => {
830                format!("result:{result}")
831            }
832            Instruction::IteratorDone { dst }
833            | Instruction::IteratorValue { dst }
834            | Instruction::IteratorResult { dst }
835            | Instruction::IteratorToArray { dst }
836            | Instruction::IteratorStackEmpty { dst }
837            | Instruction::StoreEmptyObject { dst } => {
838                format!("dst:{dst}")
839            }
840            Instruction::IteratorFinishAsyncNext { resume_kind, value } => {
841                format!("resume_kind:{resume_kind}, value:{value}")
842            }
843            Instruction::IteratorReturn { value, called } => {
844                format!("value:{value}, called:{called}")
845            }
846            Instruction::PushPrivateEnvironment {
847                class,
848                name_indices,
849            } => {
850                format!("class:{class}, names:{name_indices:?}")
851            }
852            Instruction::TemplateLookup { address, site, dst } => {
853                format!("address:{address}, site:{site}, dst:{dst}")
854            }
855            Instruction::JumpTable { index, addresses } => {
856                format!(
857                    "index:{index}, jump_table:({})",
858                    addresses.iter().format(", ")
859                )
860            }
861            Instruction::ConcatToString { dst, values } => {
862                format!("dst:{dst}, values:{values:?}")
863            }
864            Instruction::CopyDataProperties {
865                object,
866                source,
867                excluded_keys,
868            } => {
869                format!("object:{object}, source:{source}, excluded_keys:{excluded_keys:?}")
870            }
871            Instruction::TemplateCreate { site, dst, values } => {
872                format!("site:{site}, dst:{dst}, values:{values:?}")
873            }
874            Instruction::GetFunctionObject { function_object } => {
875                format!("function_object:{function_object}")
876            }
877            Instruction::Pop
878            | Instruction::DeleteSuperThrow
879            | Instruction::ReThrow
880            | Instruction::CheckReturn
881            | Instruction::Return
882            | Instruction::AsyncGeneratorClose
883            | Instruction::CreatePromiseCapability
884            | Instruction::PopEnvironment
885            | Instruction::IncrementLoopIteration
886            | Instruction::IteratorNext
887            | Instruction::SuperCallDerived
888            | Instruction::CallSpread
889            | Instruction::NewSpread
890            | Instruction::SuperCallSpread
891            | Instruction::PopPrivateEnvironment
892            | Instruction::Generator
893            | Instruction::AsyncGenerator => String::new(),
894            Instruction::CallMath { operands } => operands.to_string(),
895            Instruction::PrepareMath { operands } => operands.to_string(),
896            Instruction::Reserved3
897            | Instruction::Reserved4
898            | Instruction::Reserved5
899            | Instruction::Reserved6
900            | Instruction::Reserved7
901            | Instruction::Reserved8
902            | Instruction::Reserved9
903            | Instruction::Reserved10
904            | Instruction::Reserved11
905            | Instruction::Reserved12
906            | Instruction::Reserved13
907            | Instruction::Reserved14
908            | Instruction::Reserved15
909            | Instruction::Reserved16
910            | Instruction::Reserved17
911            | Instruction::Reserved18
912            | Instruction::Reserved19
913            | Instruction::Reserved20
914            | Instruction::Reserved21
915            | Instruction::Reserved22
916            | Instruction::Reserved23
917            | Instruction::Reserved24
918            | Instruction::Reserved25
919            | Instruction::Reserved26
920            | Instruction::Reserved27
921            | Instruction::Reserved28
922            | Instruction::Reserved29
923            | Instruction::Reserved30
924            | Instruction::Reserved31
925            | Instruction::Reserved32
926            | Instruction::Reserved33
927            | Instruction::Reserved34
928            | Instruction::Reserved35
929            | Instruction::Reserved36
930            | Instruction::Reserved37
931            | Instruction::Reserved38
932            | Instruction::Reserved39
933            | Instruction::Reserved40
934            | Instruction::Reserved41
935            | Instruction::Reserved42
936            | Instruction::Reserved43
937            | Instruction::Reserved44
938            | Instruction::Reserved45
939            | Instruction::Reserved46
940            | Instruction::Reserved47
941            | Instruction::Reserved48
942            | Instruction::Reserved49
943            | Instruction::Reserved50
944            | Instruction::Reserved51
945            | Instruction::Reserved52
946            | Instruction::Reserved53
947            | Instruction::Reserved54
948            | Instruction::Reserved55
949            | Instruction::Reserved56
950            | Instruction::Reserved57 => unreachable!("Reserved opcodes are unreachable"),
951        }
952    }
953}
954
955impl Display for CodeBlock {
956    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
957        let name = self.name();
958        writeln!(
959            f,
960            "{:-^80}",
961            format!(
962                " Compiled Output: {} ",
963                if name.is_empty() {
964                    format!("[anon#{}]", self.debug_id)
965                } else {
966                    format!("'{}'", name.to_std_string_escaped())
967                }
968            ),
969        )?;
970        writeln!(
971            f,
972            "Location     Handler      Opcode                            Operands"
973        )?;
974        let mut iterator = InstructionIterator::new(&self.bytecode);
975        while let Some((instruction_start_pc, opcode, instruction)) = iterator.next() {
976            let opcode = opcode.as_str();
977            let operands = self.instruction_operands(&instruction);
978            let pc = iterator.pc();
979            let handler = if let Some((i, handler)) = self.find_handler(instruction_start_pc as u32)
980            {
981                let border_char = if instruction_start_pc as u32 == u32::from(handler.start) {
982                    '>'
983                } else if pc as u32 == u32::from(handler.end) {
984                    '<'
985                } else {
986                    ' '
987                };
988                format!("{border_char}{i:2}: {}", handler.handler())
989            } else {
990                "           ".to_string()
991            };
992            writeln!(
993                f,
994                "  {instruction_start_pc:>06x}    {handler}     {opcode:<32}  {operands}",
995            )?;
996        }
997        writeln!(
998            f,
999            "\nRegister Count: {}, Flags: {:?}",
1000            self.register_count,
1001            self.flags.get()
1002        )?;
1003        f.write_str("Constants:")?;
1004        if self.constants.is_empty() {
1005            f.write_str(" <empty>\n")?;
1006        } else {
1007            f.write_char('\n')?;
1008            for (i, value) in self.constants.iter().enumerate() {
1009                write!(f, "    {i:04}: ")?;
1010                match value {
1011                    Constant::String(v) => {
1012                        writeln!(
1013                            f,
1014                            "[STRING] \"{}\"",
1015                            v.to_std_string_escaped().escape_debug()
1016                        )?;
1017                    }
1018                    Constant::BigInt(v) => writeln!(f, "[BIGINT] {v}n")?,
1019                    Constant::Function(code) => writeln!(
1020                        f,
1021                        "[FUNCTION] name: '{}' (length: {})",
1022                        code.name().to_std_string_escaped(),
1023                        code.length
1024                    )?,
1025                    Constant::Scope(v) => {
1026                        writeln!(
1027                            f,
1028                            "[SCOPE] index: {}, bindings: {}",
1029                            v.scope_index(),
1030                            v.num_bindings()
1031                        )?;
1032                    }
1033                }
1034            }
1035        }
1036        f.write_str("Bindings:")?;
1037        if self.bindings.is_empty() {
1038            f.write_str(" <empty>\n")?;
1039        } else {
1040            f.write_char('\n')?;
1041            for (i, binding_locator) in self.bindings.iter().enumerate() {
1042                writeln!(
1043                    f,
1044                    "    {i:04}: {}, scope: {:?}",
1045                    binding_locator.name().to_std_string_escaped(),
1046                    binding_locator.scope()
1047                )?;
1048            }
1049        }
1050        f.write_str("Handlers:")?;
1051        if self.handlers.is_empty() {
1052            f.write_str(" <empty>\n")?;
1053        } else {
1054            f.write_char('\n')?;
1055            for (i, handler) in self.handlers.iter().enumerate() {
1056                writeln!(
1057                    f,
1058                    "    {i:04}: Range: [{:04}, {:04}): Handler: {:04}, Environment: {:02}",
1059                    handler.start,
1060                    handler.end,
1061                    handler.handler(),
1062                    handler.environment_count,
1063                )?;
1064            }
1065        }
1066        f.write_str("Source Map:")?;
1067        if self.source_info().map().entries().is_empty() {
1068            f.write_str(" <empty>\n")?;
1069        } else {
1070            f.write_char('\n')?;
1071
1072            let bytecode_len = self.bytecode.bytes.len() as u32;
1073            for (i, handler) in self.source_info().map().entries().windows(2).enumerate() {
1074                let current = handler[0];
1075                let next = handler.get(1);
1076
1077                write!(
1078                    f,
1079                    "    {i:04}: {:?}: ",
1080                    current.pc..next.map_or(bytecode_len, |entry| entry.pc),
1081                )?;
1082
1083                if let Some(position) = current.position {
1084                    writeln!(
1085                        f,
1086                        "({}, {})",
1087                        position.line_number(),
1088                        position.column_number()
1089                    )?;
1090                } else {
1091                    f.write_str("unknown")?;
1092                }
1093            }
1094        }
1095        Ok(())
1096    }
1097}
1098
1099/// Creates a new function object.
1100///
1101/// This is used in cases that the prototype is not known if it's [`None`] or [`Some`].
1102///
1103/// If the prototype given is [`None`] it will use [`create_function_object_fast`]. Otherwise
1104/// it will construct the function from template objects that have all the fields except the
1105/// prototype, and will perform a prototype transition change to set the prototype.
1106///
1107/// This is slower than direct object template construction that is done in [`create_function_object_fast`].
1108pub(crate) fn create_function_object(
1109    code: Gc<CodeBlock>,
1110    prototype: JsObject,
1111    context: &mut Context,
1112) -> JsObject {
1113    let name: JsValue = code.name().clone().into();
1114    let length: JsValue = code.length.into();
1115
1116    let script_or_module = context.get_active_script_or_module();
1117
1118    let is_async = code.is_async();
1119    let is_generator = code.is_generator();
1120    let function = OrdinaryFunction::new(
1121        code,
1122        context.vm.frame().environments.snapshot_for_closure(),
1123        script_or_module,
1124        context.realm().clone(),
1125    );
1126
1127    let templates = context.intrinsics().templates();
1128
1129    let (mut template, storage, constructor_prototype) = if is_generator {
1130        let prototype = JsObject::from_proto_and_data_with_shared_shape(
1131            context.root_shape(),
1132            if is_async {
1133                context.intrinsics().objects().async_generator()
1134            } else {
1135                context.intrinsics().objects().generator()
1136            },
1137            OrdinaryObject,
1138        );
1139
1140        (
1141            templates.function_with_prototype_without_proto().clone(),
1142            vec![length, name, prototype.into()],
1143            None,
1144        )
1145    } else if is_async {
1146        (
1147            templates.function_without_proto().clone(),
1148            vec![length, name],
1149            None,
1150        )
1151    } else {
1152        let constructor_prototype = templates
1153            .function_prototype()
1154            .create(OrdinaryObject, vec![JsValue::undefined()]);
1155
1156        let template = templates.function_with_prototype_without_proto();
1157
1158        (
1159            template.clone(),
1160            vec![length, name, constructor_prototype.clone().into()],
1161            Some(constructor_prototype),
1162        )
1163    };
1164
1165    template.set_prototype(prototype);
1166
1167    let constructor = template.create(function, storage);
1168
1169    if let Some(constructor_prototype) = &constructor_prototype {
1170        constructor_prototype.borrow_mut().properties_mut().storage[0] = constructor.clone().into();
1171    }
1172    constructor
1173}
1174
1175/// Creates a new function object.
1176///
1177/// This is preferred over [`create_function_object`] if prototype is [`None`],
1178/// because it constructs the function from a pre-initialized object template,
1179/// with all the properties and prototype set.
1180pub(crate) fn create_function_object_fast(code: Gc<CodeBlock>, context: &mut Context) -> JsObject {
1181    let name: JsValue = code.name().clone().into();
1182    let length: JsValue = code.length.into();
1183
1184    let script_or_module = context.get_active_script_or_module();
1185
1186    let is_async = code.is_async();
1187    let is_generator = code.is_generator();
1188    let has_prototype_property = code.has_prototype_property();
1189    let function = OrdinaryFunction::new(
1190        code,
1191        context.vm.frame().environments.snapshot_for_closure(),
1192        script_or_module,
1193        context.realm().clone(),
1194    );
1195
1196    if is_generator {
1197        let prototype = JsObject::from_proto_and_data_with_shared_shape(
1198            context.root_shape(),
1199            if is_async {
1200                context.intrinsics().objects().async_generator()
1201            } else {
1202                context.intrinsics().objects().generator()
1203            },
1204            OrdinaryObject,
1205        );
1206        let template = if is_async {
1207            context.intrinsics().templates().async_generator_function()
1208        } else {
1209            context.intrinsics().templates().generator_function()
1210        };
1211
1212        template.create(function, vec![length, name, prototype.into()])
1213    } else if is_async {
1214        context
1215            .intrinsics()
1216            .templates()
1217            .async_function()
1218            .create(function, vec![length, name])
1219    } else if !has_prototype_property {
1220        context
1221            .intrinsics()
1222            .templates()
1223            .function()
1224            .create(function, vec![length, name])
1225    } else {
1226        let prototype = context
1227            .intrinsics()
1228            .templates()
1229            .function_prototype()
1230            .create(OrdinaryObject, vec![JsValue::undefined()]);
1231
1232        let constructor = context
1233            .intrinsics()
1234            .templates()
1235            .function_with_prototype()
1236            .create(function, vec![length, name, prototype.clone().into()]);
1237
1238        prototype.borrow_mut().properties_mut().storage[0] = constructor.clone().into();
1239
1240        constructor
1241    }
1242}