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