1use 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 #[derive(Clone, Copy, Debug, Finalize)]
29 pub(crate) struct CodeBlockFlags: u16 {
30 const STRICT = 0b0000_0001;
32
33 const HAS_BINDING_IDENTIFIER = 0b0000_0010;
35
36 const IS_CLASS_CONSTRUCTOR = 0b0000_0100;
38
39 const IN_CLASS_FIELD_INITIALIZER = 0b0000_1000;
41
42 const IS_DERIVED_CONSTRUCTOR = 0b0001_0000;
44
45 const IS_ASYNC = 0b0010_0000;
46 const IS_GENERATOR = 0b0100_0000;
47
48 const HAS_PROTOTYPE_PROPERTY = 0b1000_0000;
50
51 const HAS_FUNCTION_SCOPE = 0b1_0000_0000;
53
54 #[cfg(feature = "trace")]
56 const TRACEABLE = 0b1000_0000_0000_0000;
57 }
58}
59
60impl CodeBlockFlags {
61 #[must_use]
63 pub(crate) fn has_function_scope(self) -> bool {
64 self.contains(Self::HAS_FUNCTION_SCOPE)
65 }
66}
67
68unsafe impl Trace for CodeBlockFlags {
70 empty_trace!();
71}
72
73#[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 pub(crate) const fn handler(&self) -> Address {
90 self.end
91 }
92
93 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 String(JsString),
103 Function(Gc<CodeBlock>),
104 BigInt(#[unsafe_ignore_trace] JsBigInt),
105
106 Scope(#[unsafe_ignore_trace] Scope),
109}
110
111#[derive(Copy, Clone, Debug, Trace, Finalize)]
113#[boa_gc(empty_trace)]
114pub(crate) struct GlobalFunctionBinding {
115 pub(crate) name_index: u32,
117 pub(crate) function_index: u32,
119}
120
121#[derive(Clone, Debug, Trace, Finalize)]
127pub struct CodeBlock {
128 #[unsafe_ignore_trace]
129 pub(crate) flags: Cell<CodeBlockFlags>,
130
131 pub(crate) length: u32,
133
134 pub(crate) parameter_length: u32,
135
136 pub(crate) register_count: u32,
137
138 pub(crate) this_mode: ThisMode,
140
141 #[unsafe_ignore_trace]
143 pub(crate) mapped_arguments_binding_indices: ThinVec<Option<u32>>,
144
145 #[unsafe_ignore_trace]
147 pub(crate) bytecode: Bytecode,
148
149 pub(crate) constants: ThinVec<Constant>,
150
151 #[unsafe_ignore_trace]
153 pub(crate) bindings: Box<[BindingLocator]>,
154
155 #[unsafe_ignore_trace]
157 pub(crate) handlers: ThinVec<Handler>,
158
159 pub(crate) ic: Box<[InlineCache]>,
161
162 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 pub(crate) debug_id: u64,
171
172 #[cfg(feature = "trace")]
173 #[unsafe_ignore_trace]
174 pub(crate) traced: Cell<bool>,
175}
176
177impl CodeBlock {
179 #[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 #[must_use]
212 pub fn name(&self) -> &JsString {
213 self.source_info.function_name()
214 }
215
216 #[must_use]
218 pub fn path(&self) -> &SourcePath {
219 self.source_info.map().path()
220 }
221
222 #[cfg(feature = "trace")]
224 pub(crate) fn traceable(&self) -> bool {
225 self.flags.get().contains(CodeBlockFlags::TRACEABLE)
226 }
227 #[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 pub(crate) fn is_class_constructor(&self) -> bool {
238 self.flags
239 .get()
240 .contains(CodeBlockFlags::IS_CLASS_CONSTRUCTOR)
241 }
242
243 pub(crate) fn strict(&self) -> bool {
245 self.flags.get().contains(CodeBlockFlags::STRICT)
246 }
247
248 pub(crate) fn has_binding_identifier(&self) -> bool {
250 self.flags
251 .get()
252 .contains(CodeBlockFlags::HAS_BINDING_IDENTIFIER)
253 }
254
255 pub(crate) fn in_class_field_initializer(&self) -> bool {
257 self.flags
258 .get()
259 .contains(CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER)
260 }
261
262 pub(crate) fn is_derived_constructor(&self) -> bool {
264 self.flags
265 .get()
266 .contains(CodeBlockFlags::IS_DERIVED_CONSTRUCTOR)
267 }
268
269 pub(crate) fn is_async(&self) -> bool {
271 self.flags.get().contains(CodeBlockFlags::IS_ASYNC)
272 }
273
274 pub(crate) fn is_generator(&self) -> bool {
276 self.flags.get().contains(CodeBlockFlags::IS_GENERATOR)
277 }
278
279 pub(crate) fn is_async_generator(&self) -> bool {
281 self.flags
282 .get()
283 .contains(CodeBlockFlags::IS_ASYNC | CodeBlockFlags::IS_GENERATOR)
284 }
285
286 pub(crate) fn is_ordinary(&self) -> bool {
288 !self.is_async() && !self.is_generator()
289 }
290
291 pub(crate) fn has_prototype_property(&self) -> bool {
293 self.flags
294 .get()
295 .contains(CodeBlockFlags::HAS_PROTOTYPE_PROPERTY)
296 }
297
298 pub(crate) fn has_function_scope(&self) -> bool {
300 self.flags.get().has_function_scope()
301 }
302
303 #[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 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 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 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
372impl CodeBlock {
374 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
1086pub(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
1162pub(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}