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    builtins::{
7        function::{OrdinaryFunction, ThisMode},
8        OrdinaryObject,
9    },
10    object::JsObject,
11    Context, JsBigInt, JsString, JsValue,
12};
13use bitflags::bitflags;
14use boa_ast::scope::{BindingLocator, Scope};
15use boa_gc::{empty_trace, Finalize, Gc, Trace};
16use boa_profiler::Profiler;
17use std::{cell::Cell, fmt::Display, mem::size_of};
18use thin_vec::ThinVec;
19
20use super::{InlineCache, Instruction, InstructionIterator};
21
22/// This represents whether a value can be read from [`CodeBlock`] code.
23///
24/// # Safety
25///
26/// This trait is safe to implement as long as the type doesn't implement `Drop`.
27/// At some point, if [negative impls][negative_impls] are stabilized, we might be able to remove
28/// the unsafe bound.
29///
30/// [negative_impls]: https://doc.rust-lang.org/beta/unstable-book/language-features/negative-impls.html
31pub(crate) unsafe trait Readable {}
32
33unsafe impl Readable for u8 {}
34unsafe impl Readable for i8 {}
35unsafe impl Readable for u16 {}
36unsafe impl Readable for i16 {}
37unsafe impl Readable for u32 {}
38unsafe impl Readable for i32 {}
39unsafe impl Readable for u64 {}
40unsafe impl Readable for i64 {}
41unsafe impl Readable for f32 {}
42unsafe impl Readable for f64 {}
43
44bitflags! {
45    /// Flags for [`CodeBlock`].
46    #[derive(Clone, Copy, Debug, Finalize)]
47    pub(crate) struct CodeBlockFlags: u16 {
48        /// Is this function in strict mode.
49        const STRICT = 0b0000_0001;
50
51        /// Indicates if the function is an expression and has a binding identifier.
52        const HAS_BINDING_IDENTIFIER = 0b0000_0010;
53
54        /// The `[[IsClassConstructor]]` internal slot.
55        const IS_CLASS_CONSTRUCTOR = 0b0000_0100;
56
57        /// The `[[ClassFieldInitializerName]]` internal slot.
58        const IN_CLASS_FIELD_INITIALIZER = 0b0000_1000;
59
60        /// `[[ConstructorKind]]`
61        const IS_DERIVED_CONSTRUCTOR = 0b0001_0000;
62
63        const IS_ASYNC = 0b0010_0000;
64        const IS_GENERATOR = 0b0100_0000;
65
66        /// Arrow and method functions don't have `"prototype"` property.
67        const HAS_PROTOTYPE_PROPERTY = 0b1000_0000;
68
69        /// If the function requires a function scope.
70        const HAS_FUNCTION_SCOPE = 0b1_0000_0000;
71
72        /// Trace instruction execution to `stdout`.
73        #[cfg(feature = "trace")]
74        const TRACEABLE = 0b1000_0000_0000_0000;
75    }
76}
77
78// SAFETY: Nothing in CodeBlockFlags needs tracing, so this is safe.
79unsafe impl Trace for CodeBlockFlags {
80    empty_trace!();
81}
82
83/// This represents a range in the code that handles exception throws.
84///
85/// When a throw happens, we search for handler in the [`CodeBlock`] using
86/// the [`CodeBlock::find_handler()`] method.
87///
88/// If any exception happens and gets cought by this handler, the `pc` will be set to `end` of the
89/// [`Handler`] and remove any environments or stack values that where pushed after the handler.
90#[derive(Debug, Clone, Copy)]
91pub(crate) struct Handler {
92    pub(crate) start: u32,
93    pub(crate) end: u32,
94
95    pub(crate) stack_count: u32,
96    pub(crate) environment_count: u32,
97}
98
99impl Handler {
100    /// Get the handler address.
101    pub(crate) const fn handler(&self) -> u32 {
102        self.end
103    }
104
105    /// Check if the provided `pc` is contained in the handler range.
106    pub(crate) const fn contains(&self, pc: u32) -> bool {
107        pc < self.end && pc >= self.start
108    }
109}
110
111#[derive(Clone, Debug, Trace, Finalize)]
112pub(crate) enum Constant {
113    /// Property field names and private names `[[description]]`s.
114    String(JsString),
115    Function(Gc<CodeBlock>),
116    BigInt(#[unsafe_ignore_trace] JsBigInt),
117
118    /// Declarative or function scope.
119    // Safety: Nothing in `Scope` needs tracing, so this is safe.
120    Scope(#[unsafe_ignore_trace] Scope),
121}
122
123/// The internal representation of a JavaScript function.
124///
125/// A `CodeBlock` is generated for each function compiled by the
126/// [`ByteCompiler`](crate::bytecompiler::ByteCompiler). It stores the bytecode and the other
127/// attributes of the function.
128#[derive(Clone, Debug, Trace, Finalize)]
129pub struct CodeBlock {
130    /// Name of this function
131    #[unsafe_ignore_trace]
132    pub(crate) name: JsString,
133
134    #[unsafe_ignore_trace]
135    pub(crate) flags: Cell<CodeBlockFlags>,
136
137    /// The number of arguments expected.
138    pub(crate) length: u32,
139
140    pub(crate) parameter_length: u32,
141
142    pub(crate) register_count: u32,
143
144    /// `[[ThisMode]]`
145    pub(crate) this_mode: ThisMode,
146
147    /// Used for constructing a `MappedArguments` object.
148    #[unsafe_ignore_trace]
149    pub(crate) mapped_arguments_binding_indices: ThinVec<Option<u32>>,
150
151    /// Bytecode
152    #[unsafe_ignore_trace]
153    pub(crate) bytecode: Box<[u8]>,
154
155    pub(crate) constants: ThinVec<Constant>,
156
157    /// Locators for all bindings in the codeblock.
158    #[unsafe_ignore_trace]
159    pub(crate) bindings: Box<[BindingLocator]>,
160
161    pub(crate) local_bindings_initialized: Box<[bool]>,
162
163    /// Exception [`Handler`]s.
164    #[unsafe_ignore_trace]
165    pub(crate) handlers: ThinVec<Handler>,
166
167    /// inline caching
168    pub(crate) ic: Box<[InlineCache]>,
169}
170
171/// ---- `CodeBlock` public API ----
172impl CodeBlock {
173    /// Creates a new `CodeBlock`.
174    #[must_use]
175    pub fn new(name: JsString, length: u32, strict: bool) -> Self {
176        let mut flags = CodeBlockFlags::empty();
177        flags.set(CodeBlockFlags::STRICT, strict);
178        Self {
179            bytecode: Box::default(),
180            constants: ThinVec::default(),
181            bindings: Box::default(),
182            local_bindings_initialized: Box::default(),
183            name,
184            flags: Cell::new(flags),
185            length,
186            register_count: 0,
187            this_mode: ThisMode::Global,
188            mapped_arguments_binding_indices: ThinVec::new(),
189            parameter_length: 0,
190            handlers: ThinVec::default(),
191            ic: Box::default(),
192        }
193    }
194
195    /// Retrieves the name associated with this code block.
196    #[must_use]
197    pub const fn name(&self) -> &JsString {
198        &self.name
199    }
200
201    /// Check if the function is traced.
202    #[cfg(feature = "trace")]
203    pub(crate) fn traceable(&self) -> bool {
204        self.flags.get().contains(CodeBlockFlags::TRACEABLE)
205    }
206    /// Enable or disable instruction tracing to `stdout`.
207    #[cfg(feature = "trace")]
208    #[inline]
209    pub fn set_traceable(&self, value: bool) {
210        let mut flags = self.flags.get();
211        flags.set(CodeBlockFlags::TRACEABLE, value);
212        self.flags.set(flags);
213    }
214
215    /// Check if the function is a class constructor.
216    pub(crate) fn is_class_constructor(&self) -> bool {
217        self.flags
218            .get()
219            .contains(CodeBlockFlags::IS_CLASS_CONSTRUCTOR)
220    }
221
222    /// Check if the function is in strict mode.
223    pub(crate) fn strict(&self) -> bool {
224        self.flags.get().contains(CodeBlockFlags::STRICT)
225    }
226
227    /// Indicates if the function is an expression and has a binding identifier.
228    pub(crate) fn has_binding_identifier(&self) -> bool {
229        self.flags
230            .get()
231            .contains(CodeBlockFlags::HAS_BINDING_IDENTIFIER)
232    }
233
234    /// Does this function have the `[[ClassFieldInitializerName]]` internal slot set to non-empty value.
235    pub(crate) fn in_class_field_initializer(&self) -> bool {
236        self.flags
237            .get()
238            .contains(CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER)
239    }
240
241    /// Returns true if this function is a derived constructor.
242    pub(crate) fn is_derived_constructor(&self) -> bool {
243        self.flags
244            .get()
245            .contains(CodeBlockFlags::IS_DERIVED_CONSTRUCTOR)
246    }
247
248    /// Returns true if this function an async function.
249    pub(crate) fn is_async(&self) -> bool {
250        self.flags.get().contains(CodeBlockFlags::IS_ASYNC)
251    }
252
253    /// Returns true if this function an generator function.
254    pub(crate) fn is_generator(&self) -> bool {
255        self.flags.get().contains(CodeBlockFlags::IS_GENERATOR)
256    }
257
258    /// Returns true if this function a async generator function.
259    pub(crate) fn is_async_generator(&self) -> bool {
260        self.flags
261            .get()
262            .contains(CodeBlockFlags::IS_ASYNC | CodeBlockFlags::IS_GENERATOR)
263    }
264
265    /// Returns true if this function an async function.
266    pub(crate) fn is_ordinary(&self) -> bool {
267        !self.is_async() && !self.is_generator()
268    }
269
270    /// Returns true if this function has the `"prototype"` property when function object is created.
271    pub(crate) fn has_prototype_property(&self) -> bool {
272        self.flags
273            .get()
274            .contains(CodeBlockFlags::HAS_PROTOTYPE_PROPERTY)
275    }
276
277    /// Returns true if this function requires a function scope.
278    pub(crate) fn has_function_scope(&self) -> bool {
279        self.flags
280            .get()
281            .contains(CodeBlockFlags::HAS_FUNCTION_SCOPE)
282    }
283
284    /// Find exception [`Handler`] in the code block given the current program counter (`pc`).
285    #[inline]
286    pub(crate) fn find_handler(&self, pc: u32) -> Option<(usize, &Handler)> {
287        self.handlers
288            .iter()
289            .enumerate()
290            .rev()
291            .find(|(_, handler)| handler.contains(pc))
292    }
293
294    /// Get the [`JsString`] constant from the [`CodeBlock`].
295    ///
296    /// # Panics
297    ///
298    /// If the type of the [`Constant`] is not [`Constant::String`].
299    /// Or `index` is greater or equal to length of `constants`.
300    pub(crate) fn constant_string(&self, index: usize) -> JsString {
301        if let Some(Constant::String(value)) = self.constants.get(index) {
302            return value.clone();
303        }
304
305        panic!("expected string constant at index {index}")
306    }
307
308    /// Get the function ([`Gc<CodeBlock>`]) constant from the [`CodeBlock`].
309    ///
310    /// # Panics
311    ///
312    /// If the type of the [`Constant`] is not [`Constant::Function`].
313    /// Or `index` is greater or equal to length of `constants`.
314    pub(crate) fn constant_function(&self, index: usize) -> Gc<Self> {
315        if let Some(Constant::Function(value)) = self.constants.get(index) {
316            return value.clone();
317        }
318
319        panic!("expected function constant at index {index}")
320    }
321
322    /// Get the [`Scope`] constant from the [`CodeBlock`].
323    ///
324    /// # Panics
325    ///
326    /// If the type of the [`Constant`] is not [`Constant::Scope`].
327    /// Or `index` is greater or equal to length of `constants`.
328    pub(crate) fn constant_scope(&self, index: usize) -> Scope {
329        if let Some(Constant::Scope(value)) = self.constants.get(index) {
330            return value.clone();
331        }
332
333        panic!("expected scope constant at index {index}")
334    }
335}
336
337/// ---- `CodeBlock` private API ----
338impl CodeBlock {
339    /// Read type T from code.
340    ///
341    /// # Safety
342    ///
343    /// Does not check if read happens out-of-bounds.
344    pub(crate) const unsafe fn read_unchecked<T>(&self, offset: usize) -> T
345    where
346        T: Readable,
347    {
348        // Safety:
349        // The function caller must ensure that the read is in bounds.
350        //
351        // This has to be an unaligned read because we can't guarantee that
352        // the types are aligned.
353        unsafe {
354            self.bytecode
355                .as_ptr()
356                .add(offset)
357                .cast::<T>()
358                .read_unaligned()
359        }
360    }
361
362    /// Read type T from code.
363    #[track_caller]
364    pub(crate) fn read<T>(&self, offset: usize) -> T
365    where
366        T: Readable,
367    {
368        assert!(offset + size_of::<T>() - 1 < self.bytecode.len());
369
370        // Safety: We checked that it is not an out-of-bounds read,
371        // so this is safe.
372        unsafe { self.read_unchecked(offset) }
373    }
374
375    /// Get the operands after the `Opcode` pointed to by `pc` as a `String`.
376    /// Modifies the `pc` to point to the next instruction.
377    ///
378    /// Returns an empty `String` if no operands are present.
379    pub(crate) fn instruction_operands(&self, instruction: &Instruction) -> String {
380        match instruction {
381            Instruction::SetFunctionName { prefix } => match prefix {
382                0 => "prefix: none",
383                1 => "prefix: get",
384                2 => "prefix: set",
385                _ => unreachable!(),
386            }
387            .to_owned(),
388            Instruction::RotateLeft { n } | Instruction::RotateRight { n } => n.to_string(),
389            Instruction::Generator { r#async } => {
390                format!("async: {async}")
391            }
392            Instruction::PushInt8 { value } => value.to_string(),
393            Instruction::PushInt16 { value } => value.to_string(),
394            Instruction::PushInt32 { value } => value.to_string(),
395            Instruction::PushFloat { value } => ryu_js::Buffer::new().format(*value).to_string(),
396            Instruction::PushDouble { value } => ryu_js::Buffer::new().format(*value).to_string(),
397            Instruction::PushLiteral { index }
398            | Instruction::ThrowNewTypeError { message: index }
399            | Instruction::ThrowNewSyntaxError { message: index }
400            | Instruction::HasRestrictedGlobalProperty { index }
401            | Instruction::CanDeclareGlobalFunction { index }
402            | Instruction::CanDeclareGlobalVar { index } => index.value().to_string(),
403            Instruction::PushRegExp {
404                pattern_index: source_index,
405                flags_index: flag_index,
406            } => {
407                let pattern = self
408                    .constant_string(source_index.value() as usize)
409                    .to_std_string_escaped();
410                let flags = self
411                    .constant_string(flag_index.value() as usize)
412                    .to_std_string_escaped();
413                format!("/{pattern}/{flags}")
414            }
415            Instruction::Jump { address: value }
416            | Instruction::JumpIfTrue { address: value }
417            | Instruction::JumpIfFalse { address: value }
418            | Instruction::JumpIfNotUndefined { address: value }
419            | Instruction::JumpIfNullOrUndefined { address: value }
420            | Instruction::Case { address: value }
421            | Instruction::Default { address: value }
422            | Instruction::LogicalAnd { exit: value }
423            | Instruction::LogicalOr { exit: value }
424            | Instruction::Coalesce { exit: value } => value.to_string(),
425            Instruction::CallEval {
426                argument_count: value,
427                scope_index,
428            } => {
429                format!("{}, {}", value.value(), scope_index.value())
430            }
431            Instruction::Call {
432                argument_count: value,
433            }
434            | Instruction::New {
435                argument_count: value,
436            }
437            | Instruction::SuperCall {
438                argument_count: value,
439            }
440            | Instruction::ConcatToString { value_count: value }
441            | Instruction::GetArgument { index: value } => value.value().to_string(),
442            Instruction::PushScope { index } | Instruction::CallEvalSpread { index } => {
443                index.value().to_string()
444            }
445            Instruction::CopyDataProperties {
446                excluded_key_count: value1,
447                excluded_key_count_computed: value2,
448            } => format!("{}, {}", value1.value(), value2.value()),
449            Instruction::GeneratorDelegateNext {
450                return_method_undefined: value1,
451                throw_method_undefined: value2,
452            }
453            | Instruction::GeneratorDelegateResume {
454                exit: value1,
455                r#return: value2,
456            } => {
457                format!("{value1}, {value2}")
458            }
459            Instruction::TemplateLookup { exit: value, site } => format!("{value}, {site}"),
460            Instruction::TemplateCreate { count, site } => {
461                format!("{}, {site}", count.value())
462            }
463            Instruction::GetFunction { index } => {
464                let index = index.value() as usize;
465                format!(
466                    "{index:04}: '{}' (length: {})",
467                    self.constant_function(index).name().to_std_string_escaped(),
468                    self.constant_function(index).length
469                )
470            }
471            Instruction::DefVar { index }
472            | Instruction::DefInitVar { index }
473            | Instruction::PutLexicalValue { index }
474            | Instruction::GetName { index }
475            | Instruction::GetLocator { index }
476            | Instruction::GetNameAndLocator { index }
477            | Instruction::GetNameOrUndefined { index }
478            | Instruction::SetName { index }
479            | Instruction::DeleteName { index } => {
480                format!(
481                    "{:04}: '{}'",
482                    index.value(),
483                    self.bindings[index.value() as usize]
484                        .name()
485                        .to_std_string_escaped()
486                )
487            }
488            Instruction::DefineOwnPropertyByName { index }
489            | Instruction::DefineClassStaticMethodByName { index }
490            | Instruction::DefineClassMethodByName { index }
491            | Instruction::SetPropertyGetterByName { index }
492            | Instruction::DefineClassStaticGetterByName { index }
493            | Instruction::DefineClassGetterByName { index }
494            | Instruction::SetPropertySetterByName { index }
495            | Instruction::DefineClassStaticSetterByName { index }
496            | Instruction::DefineClassSetterByName { index }
497            | Instruction::InPrivate { index }
498            | Instruction::ThrowMutateImmutable { index }
499            | Instruction::DeletePropertyByName { index }
500            | Instruction::SetPrivateField { index }
501            | Instruction::DefinePrivateField { index }
502            | Instruction::SetPrivateMethod { index }
503            | Instruction::SetPrivateSetter { index }
504            | Instruction::SetPrivateGetter { index }
505            | Instruction::GetPrivateField { index }
506            | Instruction::PushClassFieldPrivate { index }
507            | Instruction::PushClassPrivateGetter { index }
508            | Instruction::PushClassPrivateSetter { index }
509            | Instruction::PushClassPrivateMethod { index } => {
510                format!(
511                    "{:04}: '{}'",
512                    index.value(),
513                    self.constant_string(index.value() as usize)
514                        .to_std_string_escaped(),
515                )
516            }
517            Instruction::GetPropertyByName { index } | Instruction::SetPropertyByName { index } => {
518                let ic = &self.ic[index.value() as usize];
519                let slot = ic.slot();
520                format!(
521                    "{:04}: '{}', Shape: 0x{:x}, Slot: index: {}, attributes {:?}",
522                    index.value(),
523                    ic.name.to_std_string_escaped(),
524                    ic.shape.borrow().to_addr_usize(),
525                    slot.index,
526                    slot.attributes,
527                )
528            }
529            Instruction::PushPrivateEnvironment { name_indices } => {
530                format!("{name_indices:?}")
531            }
532            Instruction::JumpTable { default, addresses } => {
533                let mut operands = format!("#{}: Default: {default:4}", addresses.len());
534                for (i, address) in addresses.iter().enumerate() {
535                    operands += &format!(", {i}: {address}");
536                }
537                operands
538            }
539            Instruction::JumpIfNotResumeKind { exit, resume_kind } => {
540                format!("ResumeKind: {resume_kind:?}, exit: {exit}")
541            }
542            Instruction::CreateIteratorResult { done } => {
543                format!("done: {done}")
544            }
545            Instruction::CreateGlobalFunctionBinding {
546                index,
547                configurable,
548            }
549            | Instruction::CreateGlobalVarBinding {
550                index,
551                configurable,
552            } => {
553                let name = self
554                    .constant_string(index.value() as usize)
555                    .to_std_string_escaped();
556                format!("name: {name}, configurable: {configurable}")
557            }
558            Instruction::PushClassField {
559                is_anonymous_function,
560            } => {
561                format!("is_anonymous_function: {is_anonymous_function}")
562            }
563            Instruction::PopIntoRegister { dst } | Instruction::PopIntoLocal { dst } => {
564                format!("dst:reg{}", dst.value())
565            }
566            Instruction::PushFromRegister { src } | Instruction::PushFromLocal { src } => {
567                format!("src:reg{}", src.value())
568            }
569            Instruction::Pop
570            | Instruction::Dup
571            | Instruction::Swap
572            | Instruction::PushZero
573            | Instruction::PushOne
574            | Instruction::PushNaN
575            | Instruction::PushPositiveInfinity
576            | Instruction::PushNegativeInfinity
577            | Instruction::PushNull
578            | Instruction::PushTrue
579            | Instruction::PushFalse
580            | Instruction::PushUndefined
581            | Instruction::PushEmptyObject
582            | Instruction::PushClassPrototype
583            | Instruction::SetClassPrototype
584            | Instruction::SetHomeObject
585            | Instruction::Add
586            | Instruction::Sub
587            | Instruction::Div
588            | Instruction::Mul
589            | Instruction::Mod
590            | Instruction::Pow
591            | Instruction::ShiftRight
592            | Instruction::ShiftLeft
593            | Instruction::UnsignedShiftRight
594            | Instruction::BitOr
595            | Instruction::BitAnd
596            | Instruction::BitXor
597            | Instruction::BitNot
598            | Instruction::In
599            | Instruction::Eq
600            | Instruction::StrictEq
601            | Instruction::NotEq
602            | Instruction::StrictNotEq
603            | Instruction::GreaterThan
604            | Instruction::GreaterThanOrEq
605            | Instruction::LessThan
606            | Instruction::LessThanOrEq
607            | Instruction::InstanceOf
608            | Instruction::TypeOf
609            | Instruction::Void
610            | Instruction::LogicalNot
611            | Instruction::Pos
612            | Instruction::Neg
613            | Instruction::Inc
614            | Instruction::IncPost
615            | Instruction::Dec
616            | Instruction::DecPost
617            | Instruction::GetPropertyByValue
618            | Instruction::GetPropertyByValuePush
619            | Instruction::SetPropertyByValue
620            | Instruction::DefineOwnPropertyByValue
621            | Instruction::DefineClassStaticMethodByValue
622            | Instruction::DefineClassMethodByValue
623            | Instruction::SetPropertyGetterByValue
624            | Instruction::DefineClassStaticGetterByValue
625            | Instruction::DefineClassGetterByValue
626            | Instruction::SetPropertySetterByValue
627            | Instruction::DefineClassStaticSetterByValue
628            | Instruction::DefineClassSetterByValue
629            | Instruction::DeletePropertyByValue
630            | Instruction::DeleteSuperThrow
631            | Instruction::ToPropertyKey
632            | Instruction::ToBoolean
633            | Instruction::Throw
634            | Instruction::ReThrow
635            | Instruction::Exception
636            | Instruction::MaybeException
637            | Instruction::This
638            | Instruction::ThisForObjectEnvironmentName { .. }
639            | Instruction::Super
640            | Instruction::CheckReturn
641            | Instruction::Return
642            | Instruction::AsyncGeneratorClose
643            | Instruction::CreatePromiseCapability
644            | Instruction::CompletePromiseCapability
645            | Instruction::PopEnvironment
646            | Instruction::IncrementLoopIteration
647            | Instruction::CreateForInIterator
648            | Instruction::GetIterator
649            | Instruction::GetAsyncIterator
650            | Instruction::IteratorNext
651            | Instruction::IteratorNextWithoutPop
652            | Instruction::IteratorFinishAsyncNext
653            | Instruction::IteratorValue
654            | Instruction::IteratorValueWithoutPop
655            | Instruction::IteratorResult
656            | Instruction::IteratorDone
657            | Instruction::IteratorToArray
658            | Instruction::IteratorReturn
659            | Instruction::IteratorStackEmpty
660            | Instruction::RequireObjectCoercible
661            | Instruction::ValueNotNullOrUndefined
662            | Instruction::RestParameterInit
663            | Instruction::PushValueToArray
664            | Instruction::PushElisionToArray
665            | Instruction::PushIteratorToArray
666            | Instruction::PushNewArray
667            | Instruction::GeneratorYield
668            | Instruction::AsyncGeneratorYield
669            | Instruction::GeneratorNext
670            | Instruction::SuperCallDerived
671            | Instruction::Await
672            | Instruction::NewTarget
673            | Instruction::ImportMeta
674            | Instruction::SuperCallPrepare
675            | Instruction::CallSpread
676            | Instruction::NewSpread
677            | Instruction::SuperCallSpread
678            | Instruction::SetPrototype
679            | Instruction::PushObjectEnvironment
680            | Instruction::IsObject
681            | Instruction::SetNameByLocator
682            | Instruction::PopPrivateEnvironment
683            | Instruction::ImportCall
684            | Instruction::GetReturnValue
685            | Instruction::SetReturnValue
686            | Instruction::BindThisValue
687            | Instruction::CreateMappedArgumentsObject
688            | Instruction::CreateUnmappedArgumentsObject
689            | Instruction::Nop => String::new(),
690
691            Instruction::U16Operands
692            | Instruction::U32Operands
693            | Instruction::Reserved1
694            | Instruction::Reserved2
695            | Instruction::Reserved3
696            | Instruction::Reserved4
697            | Instruction::Reserved5
698            | Instruction::Reserved6
699            | Instruction::Reserved7
700            | Instruction::Reserved8
701            | Instruction::Reserved9
702            | Instruction::Reserved10
703            | Instruction::Reserved11
704            | Instruction::Reserved12
705            | Instruction::Reserved13
706            | Instruction::Reserved14
707            | Instruction::Reserved15
708            | Instruction::Reserved16
709            | Instruction::Reserved17
710            | Instruction::Reserved18
711            | Instruction::Reserved19
712            | Instruction::Reserved20
713            | Instruction::Reserved21
714            | Instruction::Reserved22
715            | Instruction::Reserved23
716            | Instruction::Reserved24
717            | Instruction::Reserved25
718            | Instruction::Reserved26
719            | Instruction::Reserved27
720            | Instruction::Reserved28
721            | Instruction::Reserved29
722            | Instruction::Reserved30
723            | Instruction::Reserved31
724            | Instruction::Reserved32
725            | Instruction::Reserved33
726            | Instruction::Reserved34
727            | Instruction::Reserved35
728            | Instruction::Reserved36
729            | Instruction::Reserved37
730            | Instruction::Reserved38
731            | Instruction::Reserved39
732            | Instruction::Reserved40
733            | Instruction::Reserved41
734            | Instruction::Reserved42
735            | Instruction::Reserved43
736            | Instruction::Reserved44
737            | Instruction::Reserved45
738            | Instruction::Reserved46
739            | Instruction::Reserved47
740            | Instruction::Reserved48
741            | Instruction::Reserved49 => unreachable!("Reserved opcodes are unrechable"),
742        }
743    }
744}
745
746impl Display for CodeBlock {
747    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
748        let name = self.name();
749
750        writeln!(
751            f,
752            "{:-^70}\nLocation  Count    Handler    Opcode                     Operands\n",
753            format!("Compiled Output: '{}'", name.to_std_string_escaped()),
754        )?;
755
756        let mut iterator = InstructionIterator::new(&self.bytecode);
757
758        let mut count = 0;
759        while let Some((instruction_start_pc, varying_operand_kind, instruction)) = iterator.next()
760        {
761            let opcode = instruction.opcode().as_str();
762            let operands = self.instruction_operands(&instruction);
763            let pc = iterator.pc();
764
765            let handler = if let Some((i, handler)) = self.find_handler(instruction_start_pc as u32)
766            {
767                let border_char = if instruction_start_pc as u32 == handler.start {
768                    '>'
769                } else if pc as u32 == handler.end {
770                    '<'
771                } else {
772                    ' '
773                };
774                format!("{border_char}{i:2}: {:04}", handler.handler())
775            } else {
776                "   none  ".to_string()
777            };
778
779            let varying_operand_kind = match varying_operand_kind {
780                super::VaryingOperandKind::U8 => "",
781                super::VaryingOperandKind::U16 => ".U16",
782                super::VaryingOperandKind::U32 => ".U32",
783            };
784
785            writeln!(
786                f,
787                    "{instruction_start_pc:06}    {count:04}   {handler}    {opcode}{varying_operand_kind:<27}{operands}",
788                )?;
789            count += 1;
790        }
791
792        f.write_str("\nConstants:")?;
793
794        if self.constants.is_empty() {
795            f.write_str(" <empty>\n")?;
796        } else {
797            f.write_str("\n")?;
798            for (i, value) in self.constants.iter().enumerate() {
799                write!(f, "    {i:04}: ")?;
800                match value {
801                    Constant::String(v) => {
802                        writeln!(
803                            f,
804                            "[STRING] \"{}\"",
805                            v.to_std_string_escaped().escape_debug()
806                        )?;
807                    }
808                    Constant::BigInt(v) => writeln!(f, "[BIGINT] {v}n")?,
809                    Constant::Function(code) => writeln!(
810                        f,
811                        "[FUNCTION] name: '{}' (length: {})\n",
812                        code.name().to_std_string_escaped(),
813                        code.length
814                    )?,
815                    Constant::Scope(v) => {
816                        writeln!(
817                            f,
818                            "[SCOPE] index: {}, bindings: {}",
819                            v.scope_index(),
820                            v.num_bindings()
821                        )?;
822                    }
823                }
824            }
825        }
826
827        f.write_str("\nBindings:\n")?;
828        if self.bindings.is_empty() {
829            f.write_str("    <empty>\n")?;
830        } else {
831            for (i, binding_locator) in self.bindings.iter().enumerate() {
832                writeln!(
833                    f,
834                    "    {i:04}: {}",
835                    binding_locator.name().to_std_string_escaped()
836                )?;
837            }
838        }
839
840        f.write_str("\nHandlers:\n")?;
841        if self.handlers.is_empty() {
842            f.write_str("    <empty>\n")?;
843        } else {
844            for (i, handler) in self.handlers.iter().enumerate() {
845                writeln!(f,
846                    "    {i:04}: Range: [{:04}, {:04}): Handler: {:04}, Stack: {:02}, Environment: {:02}",
847                    handler.start,
848                    handler.end,
849                    handler.handler(),
850                    handler.stack_count,
851                    handler.environment_count,
852                )?;
853            }
854        }
855        Ok(())
856    }
857}
858
859/// Creates a new function object.
860///
861/// This is used in cases that the prototype is not known if it's [`None`] or [`Some`].
862///
863/// If the prototype given is [`None`] it will use [`create_function_object_fast`]. Otherwise
864/// it will construct the function from template objects that have all the fields except the
865/// prototype, and will perform a prototype transition change to set the prototype.
866///
867/// This is slower than direct object template construction that is done in [`create_function_object_fast`].
868pub(crate) fn create_function_object(
869    code: Gc<CodeBlock>,
870    prototype: JsObject,
871    context: &mut Context,
872) -> JsObject {
873    let _timer = Profiler::global().start_event("create_function_object", "vm");
874
875    let name: JsValue = code.name().clone().into();
876    let length: JsValue = code.length.into();
877
878    let script_or_module = context.get_active_script_or_module();
879
880    let is_async = code.is_async();
881    let is_generator = code.is_generator();
882    let function = OrdinaryFunction::new(
883        code,
884        context.vm.environments.clone(),
885        script_or_module,
886        context.realm().clone(),
887    );
888
889    let templates = context.intrinsics().templates();
890
891    let (mut template, storage, constructor_prototype) = if is_generator {
892        let prototype = JsObject::from_proto_and_data_with_shared_shape(
893            context.root_shape(),
894            if is_async {
895                context.intrinsics().objects().async_generator()
896            } else {
897                context.intrinsics().objects().generator()
898            },
899            OrdinaryObject,
900        );
901
902        (
903            templates.function_with_prototype_without_proto().clone(),
904            vec![length, name, prototype.into()],
905            None,
906        )
907    } else if is_async {
908        (
909            templates.function_without_proto().clone(),
910            vec![length, name],
911            None,
912        )
913    } else {
914        let constructor_prototype = templates
915            .function_prototype()
916            .create(OrdinaryObject, vec![JsValue::undefined()]);
917
918        let template = templates.function_with_prototype_without_proto();
919
920        (
921            template.clone(),
922            vec![length, name, constructor_prototype.clone().into()],
923            Some(constructor_prototype),
924        )
925    };
926
927    template.set_prototype(prototype);
928
929    let constructor = template.create(function, storage);
930
931    if let Some(constructor_prototype) = &constructor_prototype {
932        constructor_prototype.borrow_mut().properties_mut().storage[0] = constructor.clone().into();
933    }
934    constructor
935}
936
937/// Creates a new function object.
938///
939/// This is prefered over [`create_function_object`] if prototype is [`None`],
940/// because it constructs the function from a pre-initialized object template,
941/// with all the properties and prototype set.
942pub(crate) fn create_function_object_fast(code: Gc<CodeBlock>, context: &mut Context) -> JsObject {
943    let _timer = Profiler::global().start_event("create_function_object_fast", "vm");
944
945    let name: JsValue = code.name().clone().into();
946    let length: JsValue = code.length.into();
947
948    let script_or_module = context.get_active_script_or_module();
949
950    let is_async = code.is_async();
951    let is_generator = code.is_generator();
952    let has_prototype_property = code.has_prototype_property();
953    let function = OrdinaryFunction::new(
954        code,
955        context.vm.environments.clone(),
956        script_or_module,
957        context.realm().clone(),
958    );
959
960    if is_generator {
961        let prototype = JsObject::from_proto_and_data_with_shared_shape(
962            context.root_shape(),
963            if is_async {
964                context.intrinsics().objects().async_generator()
965            } else {
966                context.intrinsics().objects().generator()
967            },
968            OrdinaryObject,
969        );
970        let template = if is_async {
971            context.intrinsics().templates().async_generator_function()
972        } else {
973            context.intrinsics().templates().generator_function()
974        };
975
976        template.create(function, vec![length, name, prototype.into()])
977    } else if is_async {
978        context
979            .intrinsics()
980            .templates()
981            .async_function()
982            .create(function, vec![length, name])
983    } else if !has_prototype_property {
984        context
985            .intrinsics()
986            .templates()
987            .function()
988            .create(function, vec![length, name])
989    } else {
990        let prototype = context
991            .intrinsics()
992            .templates()
993            .function_prototype()
994            .create(OrdinaryObject, vec![JsValue::undefined()]);
995
996        let constructor = context
997            .intrinsics()
998            .templates()
999            .function_with_prototype()
1000            .create(function, vec![length, name, prototype.clone().into()]);
1001
1002        prototype.borrow_mut().properties_mut().storage[0] = constructor.clone().into();
1003
1004        constructor
1005    }
1006}