1use 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
22pub(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 #[derive(Clone, Copy, Debug, Finalize)]
47 pub(crate) struct CodeBlockFlags: u16 {
48 const STRICT = 0b0000_0001;
50
51 const HAS_BINDING_IDENTIFIER = 0b0000_0010;
53
54 const IS_CLASS_CONSTRUCTOR = 0b0000_0100;
56
57 const IN_CLASS_FIELD_INITIALIZER = 0b0000_1000;
59
60 const IS_DERIVED_CONSTRUCTOR = 0b0001_0000;
62
63 const IS_ASYNC = 0b0010_0000;
64 const IS_GENERATOR = 0b0100_0000;
65
66 const HAS_PROTOTYPE_PROPERTY = 0b1000_0000;
68
69 const HAS_FUNCTION_SCOPE = 0b1_0000_0000;
71
72 #[cfg(feature = "trace")]
74 const TRACEABLE = 0b1000_0000_0000_0000;
75 }
76}
77
78unsafe impl Trace for CodeBlockFlags {
80 empty_trace!();
81}
82
83#[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 pub(crate) const fn handler(&self) -> u32 {
102 self.end
103 }
104
105 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 String(JsString),
115 Function(Gc<CodeBlock>),
116 BigInt(#[unsafe_ignore_trace] JsBigInt),
117
118 Scope(#[unsafe_ignore_trace] Scope),
121}
122
123#[derive(Clone, Debug, Trace, Finalize)]
129pub struct CodeBlock {
130 #[unsafe_ignore_trace]
132 pub(crate) name: JsString,
133
134 #[unsafe_ignore_trace]
135 pub(crate) flags: Cell<CodeBlockFlags>,
136
137 pub(crate) length: u32,
139
140 pub(crate) parameter_length: u32,
141
142 pub(crate) register_count: u32,
143
144 pub(crate) this_mode: ThisMode,
146
147 #[unsafe_ignore_trace]
149 pub(crate) mapped_arguments_binding_indices: ThinVec<Option<u32>>,
150
151 #[unsafe_ignore_trace]
153 pub(crate) bytecode: Box<[u8]>,
154
155 pub(crate) constants: ThinVec<Constant>,
156
157 #[unsafe_ignore_trace]
159 pub(crate) bindings: Box<[BindingLocator]>,
160
161 pub(crate) local_bindings_initialized: Box<[bool]>,
162
163 #[unsafe_ignore_trace]
165 pub(crate) handlers: ThinVec<Handler>,
166
167 pub(crate) ic: Box<[InlineCache]>,
169}
170
171impl CodeBlock {
173 #[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 #[must_use]
197 pub const fn name(&self) -> &JsString {
198 &self.name
199 }
200
201 #[cfg(feature = "trace")]
203 pub(crate) fn traceable(&self) -> bool {
204 self.flags.get().contains(CodeBlockFlags::TRACEABLE)
205 }
206 #[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 pub(crate) fn is_class_constructor(&self) -> bool {
217 self.flags
218 .get()
219 .contains(CodeBlockFlags::IS_CLASS_CONSTRUCTOR)
220 }
221
222 pub(crate) fn strict(&self) -> bool {
224 self.flags.get().contains(CodeBlockFlags::STRICT)
225 }
226
227 pub(crate) fn has_binding_identifier(&self) -> bool {
229 self.flags
230 .get()
231 .contains(CodeBlockFlags::HAS_BINDING_IDENTIFIER)
232 }
233
234 pub(crate) fn in_class_field_initializer(&self) -> bool {
236 self.flags
237 .get()
238 .contains(CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER)
239 }
240
241 pub(crate) fn is_derived_constructor(&self) -> bool {
243 self.flags
244 .get()
245 .contains(CodeBlockFlags::IS_DERIVED_CONSTRUCTOR)
246 }
247
248 pub(crate) fn is_async(&self) -> bool {
250 self.flags.get().contains(CodeBlockFlags::IS_ASYNC)
251 }
252
253 pub(crate) fn is_generator(&self) -> bool {
255 self.flags.get().contains(CodeBlockFlags::IS_GENERATOR)
256 }
257
258 pub(crate) fn is_async_generator(&self) -> bool {
260 self.flags
261 .get()
262 .contains(CodeBlockFlags::IS_ASYNC | CodeBlockFlags::IS_GENERATOR)
263 }
264
265 pub(crate) fn is_ordinary(&self) -> bool {
267 !self.is_async() && !self.is_generator()
268 }
269
270 pub(crate) fn has_prototype_property(&self) -> bool {
272 self.flags
273 .get()
274 .contains(CodeBlockFlags::HAS_PROTOTYPE_PROPERTY)
275 }
276
277 pub(crate) fn has_function_scope(&self) -> bool {
279 self.flags
280 .get()
281 .contains(CodeBlockFlags::HAS_FUNCTION_SCOPE)
282 }
283
284 #[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 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 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 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
337impl CodeBlock {
339 pub(crate) const unsafe fn read_unchecked<T>(&self, offset: usize) -> T
345 where
346 T: Readable,
347 {
348 unsafe {
354 self.bytecode
355 .as_ptr()
356 .add(offset)
357 .cast::<T>()
358 .read_unaligned()
359 }
360 }
361
362 #[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 unsafe { self.read_unchecked(offset) }
373 }
374
375 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
859pub(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
937pub(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}