1use std::borrow::Cow;
51use std::cell::{Cell, RefCell};
52use std::error::Error;
53use std::fmt;
54use std::sync::Arc;
55
56use bamts_bytecode::{
57 ConstantId, EcmaString, FunctionId, Instruction, Module, ModuleId, Program, Verified,
58};
59pub use bamts_native::AbiError;
60use bamts_native::{
61 Completion, CompletionTag, HelperCall, HelperResult, NativeEntryTable, NativeFrame, NativeOps,
62 ShadowFrame, Value, with_native_ops,
63};
64
65use crate::intrinsics::BuiltinOutcome;
66use crate::{
67 CalleeKind, EvalFailure, Execution, ExecutionOutcome, GeneratorResume, GeneratorStart,
68 GeneratorState, GetOutcome, HeapEntry, Host, IteratorNextPrepared, Limits, Machine,
69 PropertyMap, RuntimeError, RuntimeErrorKind, SetOutcome, SuspendedActivation, ThrowOrigin,
70 accessor_from_selector, binary_from_selector, iterator_kind_from_selector, unary_from_selector,
71};
72
73fn unary_to_selector(op: bamts_bytecode::UnaryOp) -> u32 {
81 use bamts_bytecode::UnaryOp;
82 match op {
83 UnaryOp::Void => 0,
84 UnaryOp::TypeOf => 1,
85 UnaryOp::Plus => 2,
86 UnaryOp::Negate => 3,
87 UnaryOp::BitwiseNot => 4,
88 UnaryOp::LogicalNot => 5,
89 }
90}
91
92fn binary_to_selector(op: bamts_bytecode::BinaryOp) -> u32 {
93 use bamts_bytecode::BinaryOp;
94 match op {
95 BinaryOp::Add => 0,
96 BinaryOp::Subtract => 1,
97 BinaryOp::Multiply => 2,
98 BinaryOp::Divide => 3,
99 BinaryOp::Remainder => 4,
100 BinaryOp::Exponent => 5,
101 BinaryOp::BitAnd => 6,
102 BinaryOp::BitOr => 7,
103 BinaryOp::BitXor => 8,
104 BinaryOp::ShiftLeft => 9,
105 BinaryOp::ShiftRight => 10,
106 BinaryOp::UnsignedShiftRight => 11,
107 BinaryOp::Equal => 12,
108 BinaryOp::NotEqual => 13,
109 BinaryOp::StrictEqual => 14,
110 BinaryOp::StrictNotEqual => 15,
111 BinaryOp::LessThan => 16,
112 BinaryOp::LessThanOrEqual => 17,
113 BinaryOp::GreaterThan => 18,
114 BinaryOp::GreaterThanOrEqual => 19,
115 BinaryOp::InstanceOf => 20,
116 BinaryOp::In => 21,
117 }
118}
119
120fn iterator_kind_to_selector(kind: bamts_bytecode::IteratorKind) -> u32 {
121 use bamts_bytecode::IteratorKind;
122 match kind {
123 IteratorKind::Sync => 0,
124 IteratorKind::Async => 1,
125 IteratorKind::Keys => 2,
126 }
127}
128
129fn accessor_to_selector(kind: bamts_bytecode::AccessorKind) -> u32 {
130 use bamts_bytecode::AccessorKind;
131 match kind {
132 AccessorKind::Getter => 0,
133 AccessorKind::Setter => 1,
134 }
135}
136
137#[derive(Clone, Debug, Eq, PartialEq)]
142pub enum NativeError {
143 Runtime(RuntimeError),
145 Abi(AbiError),
147 ProgramMismatch,
149 FatalTrap { value: Value },
151}
152
153impl fmt::Display for NativeError {
154 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
155 match self {
156 NativeError::Runtime(error) => write!(formatter, "{error}"),
157 NativeError::Abi(error) => write!(formatter, "{error}"),
158 NativeError::ProgramMismatch => {
159 write!(
160 formatter,
161 "native entries were compiled from different program bytes"
162 )
163 }
164 NativeError::FatalTrap { value } => {
165 write!(
166 formatter,
167 "native fatal trap (value {:#018x})",
168 value.to_bits()
169 )
170 }
171 }
172 }
173}
174
175impl Error for NativeError {}
176
177impl From<RuntimeError> for NativeError {
178 fn from(error: RuntimeError) -> Self {
179 NativeError::Runtime(error)
180 }
181}
182
183impl From<AbiError> for NativeError {
184 fn from(error: AbiError) -> Self {
185 NativeError::Abi(error)
186 }
187}
188
189#[derive(Clone, Copy, Debug, Eq, PartialEq)]
193enum Backend {
194 Reference,
196 Linked,
198}
199
200enum CodeRef<'m> {
202 Root(&'m Program<Verified>),
203 Dynamic(Arc<Program<Verified>>),
204}
205
206impl CodeRef<'_> {
207 fn code(&self, module: ModuleId) -> &Module<Verified> {
208 match self {
209 Self::Root(program) => {
210 &program
211 .module(module)
212 .expect("verified native module id remains in bounds")
213 .code
214 }
215 Self::Dynamic(program) => &program.modules()[0].code,
216 }
217 }
218}
219
220struct Activation {
224 this_value: Value,
225 new_target: Value,
226 args: Vec<Value>,
227 arguments_object: Option<Value>,
228 pending_resume: Option<Value>,
230}
231
232#[derive(Clone, Copy)]
235struct PendingThrow {
236 value: Value,
237 origin: ThrowOrigin,
238}
239
240enum Flow {
242 Next,
244 Goto(usize),
246 Unwind(Value, ThrowOrigin),
248}
249
250enum FrameCompletion {
252 Normal(Value),
253 Unwind(Value, ThrowOrigin, usize),
254 Suspend(Value, u32),
255}
256
257#[derive(Clone, Copy)]
258enum FrameDrive {
259 Ordinary,
260 GeneratorStart,
261 GeneratorResume { token: u32, sent: Value },
262}
263
264enum InvokeOutcome {
267 Value(Value),
268 Threw(Value, ThrowOrigin),
269 Fatal,
271}
272
273enum ImportFailure {
274 Threw(RuntimeError),
275 Fatal,
276}
277
278pub struct NativeEngine<'m, 'h, H: Host> {
286 machine: RefCell<Machine<'h, H>>,
289 program: &'m Program<Verified>,
291 entries: &'m dyn NativeEntryTable,
293 backend: Backend,
294 activations: RefCell<Vec<Activation>>,
296 stdout: RefCell<Vec<u8>>,
298 exit_code: Cell<i32>,
300 pending_throw: Cell<Option<PendingThrow>>,
302 pending_fatal_kind: Cell<Option<RuntimeErrorKind>>,
304 pending_error: Cell<Option<RuntimeError>>,
306 pending_abi_error: Cell<Option<AbiError>>,
308}
309
310impl<'m, 'h, H: Host> NativeEngine<'m, 'h, H> {
311 fn build(
312 program: &'m Program<Verified>,
313 entries: &'m dyn NativeEntryTable,
314 host: &'h mut H,
315 limits: Limits,
316 backend: Backend,
317 ) -> Self
318 where
319 'm: 'h,
320 {
321 let mut machine = Machine::new(program, host, limits);
322 machine.frames.clear();
323 machine.live_registers = 0;
324 NativeEngine {
325 machine: RefCell::new(machine),
326 program,
327 entries,
328 backend,
329 activations: RefCell::new(Vec::new()),
330 stdout: RefCell::new(Vec::new()),
331 exit_code: Cell::new(0),
332 pending_throw: Cell::new(None),
333 pending_fatal_kind: Cell::new(None),
334 pending_error: Cell::new(None),
335 pending_abi_error: Cell::new(None),
336 }
337 }
338
339 #[must_use]
343 pub fn new(
344 program: &'m Program<Verified>,
345 entries: &'m dyn NativeEntryTable,
346 host: &'h mut H,
347 limits: Limits,
348 ) -> Self
349 where
350 'm: 'h,
351 {
352 Self::build(program, entries, host, limits, Backend::Reference)
353 }
354
355 #[must_use]
357 pub fn stdout(&self) -> Vec<u8> {
358 self.stdout.borrow().clone()
359 }
360
361 #[must_use]
363 pub fn exit_code(&self) -> i32 {
364 self.exit_code.get()
365 }
366
367 fn max_call_depth(&self) -> usize {
368 self.machine.borrow().limits.max_call_depth
369 }
370
371 fn max_total_registers(&self) -> usize {
372 self.machine.borrow().limits.max_total_registers
373 }
374
375 fn error_at(
376 &self,
377 module: ModuleId,
378 kind: RuntimeErrorKind,
379 function: usize,
380 pc: usize,
381 ) -> RuntimeError {
382 self.machine
383 .borrow()
384 .error_at_in_module(kind, module, function, pc)
385 }
386
387 fn module(&self, module: ModuleId) -> &Module<Verified> {
388 debug_assert!((module.get() as usize) < self.machine.borrow().dynamic_base);
389 &self
390 .program
391 .module(module)
392 .expect("verified native module id remains in bounds")
393 .code
394 }
395
396 fn code_ref(&self, module: ModuleId) -> CodeRef<'m> {
397 let dynamic = {
398 let machine = self.machine.borrow();
399 let index = module.get() as usize;
400 (index >= machine.dynamic_base).then(|| {
401 machine.dynamic[index - machine.dynamic_base]
402 .program
403 .clone()
404 })
405 };
406 dynamic.map_or(CodeRef::Root(self.program), CodeRef::Dynamic)
407 }
408
409 fn is_dynamic_module(&self, module: ModuleId) -> bool {
410 module.get() as usize >= self.machine.borrow().dynamic_base
411 }
412
413 pub fn run(self) -> Result<Execution, RuntimeError> {
419 self.machine.borrow_mut().instantiate_modules()?;
420 let execution = self
421 .evaluate_reference_module(self.program.entry())?
422 .ok_or_else(|| {
423 let module = self.program.entry();
424 let function = self.module(module).entry().get() as usize;
425 self.error_at(
426 module,
427 RuntimeErrorKind::InvalidVerifiedProgram {
428 module,
429 instruction: Instruction::Halt,
430 },
431 function,
432 0,
433 )
434 })?;
435 self.machine.borrow_mut().run_to_quiescence()?;
440 Ok(execution)
441 }
442
443 fn evaluate_reference_module(
444 &self,
445 module: ModuleId,
446 ) -> Result<Option<Execution>, RuntimeError> {
447 let dependencies = match self.machine.borrow_mut().begin_module_evaluation(module)? {
448 crate::ModuleEvaluation::Cycle => return Ok(None),
449 crate::ModuleEvaluation::Evaluated(result) => return result.map(|()| None),
450 crate::ModuleEvaluation::Ready(dependencies) => dependencies,
451 };
452 for dependency in dependencies {
453 if let Err(error) = self.evaluate_reference_module(dependency) {
454 return self
455 .machine
456 .borrow_mut()
457 .finish_module_evaluation(module, Err(error))
458 .map(Some);
459 }
460 }
461
462 let code = self.module(module);
463 let function = code.entry().get() as usize;
464 let register_count = code.functions()[function].register_count() as usize;
465 let result = if self.max_call_depth() < 1 {
466 Err(self.error_at(
467 module,
468 RuntimeErrorKind::CallDepthExceeded {
469 limit: self.max_call_depth(),
470 },
471 function,
472 0,
473 ))
474 } else if register_count > self.max_total_registers() {
475 Err(self.error_at(
476 module,
477 RuntimeErrorKind::RegisterLimitExceeded {
478 limit: self.max_total_registers(),
479 },
480 function,
481 0,
482 ))
483 } else {
484 self.execute(
485 module,
486 function,
487 Value::UNDEFINED,
488 Value::UNDEFINED,
489 Vec::new(),
490 &[],
491 )
492 .and_then(|(completion, registers)| match completion {
493 FrameCompletion::Normal(value) => Ok(Execution {
494 outcome: ExecutionOutcome {
495 stdout: self.stdout.borrow().clone(),
496 exit_code: self.exit_code.get(),
497 },
498 value,
499 link: value,
500 entry_registers: registers,
501 }),
502 FrameCompletion::Unwind(value, origin, pc) => Err(self.error_at(
503 module,
504 RuntimeErrorKind::UncaughtThrow { value, origin },
505 function,
506 pc,
507 )),
508 FrameCompletion::Suspend(value, _) => Err(self.error_at(
509 module,
510 RuntimeErrorKind::InvalidValue { value },
511 function,
512 0,
513 )),
514 })
515 };
516 self.machine
517 .borrow_mut()
518 .finish_module_evaluation(module, result)
519 .map(Some)
520 }
521
522 fn seed_registers(
525 &self,
526 code: &Module<Verified>,
527 function: usize,
528 captures: &[Value],
529 args: &[Value],
530 ) -> Vec<Value> {
531 let metadata = &code.functions()[function];
532 let register_count = metadata.register_count() as usize;
533 let capture_count = metadata.capture_count() as usize;
534 let parameter_count = metadata.parameter_count() as usize;
535 let mut registers = vec![Value::UNINITIALIZED; register_count];
536 for index in 0..capture_count {
537 if let Some(slot) = registers.get_mut(index) {
538 *slot = captures.get(index).copied().unwrap_or(Value::UNDEFINED);
539 }
540 }
541 for index in 0..parameter_count {
542 if let Some(slot) = registers.get_mut(capture_count + index) {
543 *slot = args.get(index).copied().unwrap_or(Value::UNDEFINED);
544 }
545 }
546 registers
547 }
548
549 fn execute(
554 &self,
555 module: ModuleId,
556 function: usize,
557 this_value: Value,
558 new_target: Value,
559 args: Vec<Value>,
560 captures: &[Value],
561 ) -> Result<(FrameCompletion, Vec<Value>), RuntimeError> {
562 let handle = self.code_ref(module);
563 let code = handle.code(module);
564 let register_count = code.functions()[function].register_count() as usize;
565 let mut registers = self.seed_registers(code, function, captures, &args);
566 let reserved = self
567 .machine
568 .borrow_mut()
569 .reserve_native_activation(register_count);
570 reserved.map_err(|kind| self.error_at(module, kind, function, 0))?;
571 self.activations.borrow_mut().push(Activation {
572 this_value,
573 new_target,
574 args,
575 arguments_object: None,
576 pending_resume: None,
577 });
578 let completion =
579 self.run_frame(module, function, code, &mut registers, FrameDrive::Ordinary);
580 self.activations.borrow_mut().pop();
581 self.machine
582 .borrow_mut()
583 .release_native_activation(register_count);
584 completion.map(|completion| (completion, registers))
585 }
586
587 fn run_frame(
590 &self,
591 module: ModuleId,
592 function: usize,
593 code: &Module<Verified>,
594 registers: &mut Vec<Value>,
595 drive: FrameDrive,
596 ) -> Result<FrameCompletion, RuntimeError> {
597 let length = u16::try_from(registers.len()).map_err(|_| {
598 let limit = self.max_total_registers();
599 self.error_at(
600 module,
601 RuntimeErrorKind::RegisterLimitExceeded { limit },
602 function,
603 0,
604 )
605 })?;
606 let handles = registers.as_mut_ptr();
607 let mut shadow = ShadowFrame::new(std::ptr::null_mut(), 0, module.get(), handles, length);
608 let mut frame =
609 NativeFrame::new(&mut shadow, registers.as_mut_slice()).ok_or_else(|| {
610 self.error_at(
611 module,
612 RuntimeErrorKind::InvalidValue {
613 value: Value::UNDEFINED,
614 },
615 function,
616 0,
617 )
618 })?;
619
620 let target = crate::RuntimeFunction {
621 module,
622 function: FunctionId::new(function as u32),
623 };
624 let mut pc = match drive {
625 FrameDrive::Ordinary | FrameDrive::GeneratorStart => 0,
626 FrameDrive::GeneratorResume { token, sent } => {
627 let suspend_pc = token.checked_sub(1).map(|pc| pc as usize).ok_or_else(|| {
628 self.error_at(
629 module,
630 RuntimeErrorKind::InvalidValue {
631 value: Value::UNDEFINED,
632 },
633 function,
634 0,
635 )
636 })?;
637 let Instruction::Suspend { dst, resume, .. } =
638 code.functions()[function].code()[suspend_pc]
639 else {
640 return Err(self.error_at(
641 module,
642 RuntimeErrorKind::InvalidValue {
643 value: Value::UNDEFINED,
644 },
645 function,
646 suspend_pc,
647 ));
648 };
649 frame.set_register(dst.get(), sent);
650 resume.get() as usize
651 }
652 };
653 loop {
654 let instruction = code.functions()[function].code()[pc];
655 if is_inline_instruction(instruction) {
656 let consumed = self.machine.borrow_mut().consume_fuel(1);
657 if let Err(kind) = consumed {
658 return Err(self.error_at(module, kind, function, pc));
659 }
660 }
661
662 match instruction {
666 Instruction::Move { dst, src } => {
667 let value = frame.register(src.get());
668 frame.set_register(dst.get(), value);
669 pc += 1;
670 }
671 Instruction::Jump { target } => pc = target.get() as usize,
672 Instruction::JumpIfTrue { condition, target } => {
673 let value = frame.register(condition.get());
674 pc = if self.truthy(&mut frame, value) {
675 target.get() as usize
676 } else {
677 pc + 1
678 };
679 }
680 Instruction::JumpIfFalse { condition, target } => {
681 let value = frame.register(condition.get());
682 pc = if self.truthy(&mut frame, value) {
683 pc + 1
684 } else {
685 target.get() as usize
686 };
687 }
688 Instruction::Return { value } => {
689 return Ok(FrameCompletion::Normal(frame.register(value.get())));
690 }
691 Instruction::Halt => return Ok(FrameCompletion::Normal(Value::UNDEFINED)),
692 Instruction::Throw { value } => {
693 let thrown = frame.register(value.get());
694 match self.raise(
695 &mut frame,
696 code,
697 function,
698 pc,
699 thrown,
700 ThrowOrigin::Bytecode,
701 ) {
702 Flow::Next => pc += 1,
703 Flow::Goto(target) => pc = target,
704 Flow::Unwind(value, origin) => {
705 return Ok(FrameCompletion::Unwind(value, origin, pc));
706 }
707 }
708 }
709 Instruction::Suspend { src, .. }
710 if matches!(
711 drive,
712 FrameDrive::GeneratorStart | FrameDrive::GeneratorResume { .. }
713 ) =>
714 {
715 let token = u32::try_from(pc + 1).map_err(|_| {
716 self.error_at(
717 module,
718 RuntimeErrorKind::InvalidValue {
719 value: Value::UNDEFINED,
720 },
721 function,
722 pc,
723 )
724 })?;
725 return Ok(FrameCompletion::Suspend(frame.register(src.get()), token));
726 }
727 Instruction::Suspend { .. } => {
728 match self.raise(
729 &mut frame,
730 code,
731 function,
732 pc,
733 Value::UNDEFINED,
734 ThrowOrigin::TypeError {
735 operation: "suspend outside an engine-owned event loop",
736 },
737 ) {
738 Flow::Next => pc += 1,
739 Flow::Goto(target) => pc = target,
740 Flow::Unwind(value, origin) => {
741 return Ok(FrameCompletion::Unwind(value, origin, pc));
742 }
743 }
744 }
745 other => {
746 let (call, dst) = self.lower(other, &frame);
747 let result = self.dispatch(&mut frame, call);
748 match self.apply(&mut frame, target, code, pc, dst, result)? {
749 Flow::Next => pc += 1,
750 Flow::Goto(target) => pc = target,
751 Flow::Unwind(value, origin) => {
752 return Ok(FrameCompletion::Unwind(value, origin, pc));
753 }
754 }
755 }
756 }
757 }
758 }
759
760 fn lower(
764 &self,
765 instruction: Instruction,
766 frame: &NativeFrame<'_>,
767 ) -> (HelperCall, Option<u32>) {
768 let register = |r: bamts_bytecode::Register| frame.register(r.get());
769 match instruction {
770 Instruction::LoadConst { dst, constant } => (
771 HelperCall::LoadConstant {
772 const_id: constant.get(),
773 },
774 Some(dst.get()),
775 ),
776 Instruction::Unary { dst, op, operand } => (
777 HelperCall::Unary {
778 op: unary_to_selector(op),
779 operand: register(operand),
780 },
781 Some(dst.get()),
782 ),
783 Instruction::Binary {
784 dst,
785 op,
786 left,
787 right,
788 } => (
789 HelperCall::Binary {
790 op: binary_to_selector(op),
791 left: register(left),
792 right: register(right),
793 },
794 Some(dst.get()),
795 ),
796 Instruction::CreateObject { dst } => (HelperCall::CreateObject, Some(dst.get())),
797 Instruction::CreateArray { dst } => (HelperCall::CreateArray, Some(dst.get())),
798 Instruction::CreateCell { dst } => (HelperCall::CreateCell, Some(dst.get())),
799 Instruction::CreateClosure {
800 dst,
801 function,
802 captures,
803 } => (
804 HelperCall::CreateClosure {
805 function_id: function.get(),
806 captures: register(captures),
807 },
808 Some(dst.get()),
809 ),
810 Instruction::GetProperty { dst, object, key } => (
811 HelperCall::GetProperty {
812 object: register(object),
813 key: register(key),
814 },
815 Some(dst.get()),
816 ),
817 Instruction::SetProperty { object, key, value } => (
818 HelperCall::SetProperty {
819 object: register(object),
820 key: register(key),
821 value: register(value),
822 },
823 None,
824 ),
825 Instruction::DeleteProperty { dst, object, key } => (
826 HelperCall::DeleteProperty {
827 object: register(object),
828 key: register(key),
829 },
830 Some(dst.get()),
831 ),
832 Instruction::DefineAccessor {
833 object,
834 key,
835 accessor,
836 kind,
837 } => (
838 HelperCall::DefineAccessor {
839 object: register(object),
840 key: register(key),
841 accessor: register(accessor),
842 kind: accessor_to_selector(kind),
843 },
844 None,
845 ),
846 Instruction::Call {
847 dst,
848 callee,
849 this_value,
850 arguments,
851 } => (
852 HelperCall::Call {
853 callee: register(callee),
854 this_value: register(this_value),
855 arguments: register(arguments),
856 },
857 Some(dst.get()),
858 ),
859 Instruction::Construct {
860 dst,
861 callee,
862 arguments,
863 } => (
864 HelperCall::Construct {
865 callee: register(callee),
866 arguments: register(arguments),
867 },
868 Some(dst.get()),
869 ),
870 Instruction::LoadGlobal { dst, name } => {
871 (HelperCall::LoadGlobal { name: name.get() }, Some(dst.get()))
872 }
873 Instruction::StoreGlobal { name, value } => (
874 HelperCall::StoreGlobal {
875 name: name.get(),
876 value: register(value),
877 },
878 None,
879 ),
880 Instruction::TypeOfGlobal { dst, name } => (
881 HelperCall::TypeOfGlobal { name: name.get() },
882 Some(dst.get()),
883 ),
884 Instruction::LoadThis { dst } => (HelperCall::LoadThis, Some(dst.get())),
885 Instruction::LoadArguments { dst } => (HelperCall::LoadArguments, Some(dst.get())),
886 Instruction::LoadNewTarget { dst } => (HelperCall::LoadNewTarget, Some(dst.get())),
887 Instruction::ArrayPush { array, value } => (
888 HelperCall::ArrayPush {
889 array: register(array),
890 value: register(value),
891 },
892 None,
893 ),
894 Instruction::ArrayExtend { array, iterable } => (
895 HelperCall::ArrayExtend {
896 array: register(array),
897 iterable: register(iterable),
898 },
899 None,
900 ),
901 Instruction::ObjectSpread { target, source } => (
902 HelperCall::ObjectSpread {
903 target: register(target),
904 source: register(source),
905 },
906 None,
907 ),
908 Instruction::SetPrototype { object, prototype } => (
909 HelperCall::SetPrototype {
910 object: register(object),
911 prototype: register(prototype),
912 },
913 None,
914 ),
915 Instruction::CreatePrivateName { dst, description } => (
916 HelperCall::CreatePrivateName {
917 description: description.get(),
918 },
919 Some(dst.get()),
920 ),
921 Instruction::CreateRegExp {
922 dst,
923 pattern,
924 flags,
925 } => (
926 HelperCall::CreateRegExp {
927 pattern: pattern.get(),
928 flags: flags.get(),
929 },
930 Some(dst.get()),
931 ),
932 Instruction::GetIterator { dst, src, kind } => (
933 HelperCall::GetIterator {
934 src: register(src),
935 kind: iterator_kind_to_selector(kind),
936 },
937 Some(dst.get()),
938 ),
939 Instruction::IteratorNext {
940 done,
941 value,
942 iterator,
943 } => (
944 HelperCall::IteratorNext {
945 iterator: register(iterator),
946 done_reg: done.get(),
947 value_reg: value.get(),
948 },
949 None,
950 ),
951 Instruction::Import { dst, specifier } => (
952 HelperCall::Import {
953 specifier: specifier.get(),
954 },
955 Some(dst.get()),
956 ),
957 Instruction::Export { name, src } => (
958 HelperCall::Export {
959 name: name.get(),
960 src: register(src),
961 },
962 None,
963 ),
964 Instruction::Move { .. }
966 | Instruction::Jump { .. }
967 | Instruction::JumpIfTrue { .. }
968 | Instruction::JumpIfFalse { .. }
969 | Instruction::Return { .. }
970 | Instruction::Throw { .. }
971 | Instruction::Suspend { .. }
972 | Instruction::Halt => {
973 unreachable!("control-flow opcode is not lowered to a helper call")
974 }
975 }
976 }
977
978 fn take_matching_throw(&self, value: Value) -> (Value, ThrowOrigin) {
981 match self.pending_throw.take() {
982 Some(pending) if pending.value == value => (pending.value, pending.origin),
983 Some(_) | None => (value, ThrowOrigin::Bytecode),
984 }
985 }
986
987 fn apply(
988 &self,
989 frame: &mut NativeFrame<'_>,
990 target: crate::RuntimeFunction,
991 code: &Module<Verified>,
992 pc: usize,
993 dst: Option<u32>,
994 result: HelperResult,
995 ) -> Result<Flow, RuntimeError> {
996 let function = target.function.get() as usize;
997 match result.tag {
998 CompletionTag::Normal => {
999 self.pending_throw.take();
1000 if let Some(register) = dst {
1001 frame.set_register(register, result.value);
1002 }
1003 Ok(Flow::Next)
1004 }
1005 CompletionTag::Throw => {
1006 let (value, origin) = self.take_matching_throw(result.value);
1007 Ok(self.raise(frame, code, function, pc, value, origin))
1008 }
1009 CompletionTag::Suspend => {
1010 Err(self.error_at(
1013 target.module,
1014 RuntimeErrorKind::InvalidValue {
1015 value: result.value,
1016 },
1017 function,
1018 pc,
1019 ))
1020 }
1021 CompletionTag::FatalTrap => {
1022 if let Some(error) = self.pending_error.take() {
1023 return Err(error);
1024 }
1025 let kind = self.pending_fatal_kind.take().unwrap_or({
1026 RuntimeErrorKind::InvalidValue {
1027 value: result.value,
1028 }
1029 });
1030 Err(self.error_at(target.module, kind, function, pc))
1031 }
1032 }
1033 }
1034
1035 fn raise(
1038 &self,
1039 frame: &mut NativeFrame<'_>,
1040 code: &Module<Verified>,
1041 function: usize,
1042 pc: usize,
1043 value: Value,
1044 origin: ThrowOrigin,
1045 ) -> Flow {
1046 match crate::innermost_handler(&code.functions()[function], pc) {
1047 Some(handler) => {
1048 frame.set_register(handler.catch_register.get(), value);
1049 Flow::Goto(handler.handler.get() as usize)
1050 }
1051 None => Flow::Unwind(value, origin),
1052 }
1053 }
1054
1055 fn invoke_callee(
1061 &self,
1062 callee: Value,
1063 this: Value,
1064 args: &[Value],
1065 new_target: Value,
1066 ) -> InvokeOutcome {
1067 let mut callee = callee;
1068 let mut this = this;
1069 let mut args = Cow::Borrowed(args);
1070 let mut new_target = new_target;
1071 loop {
1072 let kind = self.machine.borrow().callee_kind(callee);
1073 match kind {
1074 Ok(CalleeKind::Runtime { target, captures }) => {
1075 return self.invoke_runtime(target, &captures, this, new_target, args.as_ref());
1076 }
1077 Ok(CalleeKind::Builtin { id }) => {
1078 let result = {
1079 self.machine
1080 .borrow_mut()
1081 .call_builtin(id, this, args.as_ref(), false)
1082 };
1083 match result {
1084 Ok(BuiltinOutcome::Value(value)) => {
1085 return InvokeOutcome::Value(value);
1086 }
1087 Ok(BuiltinOutcome::Call {
1088 callee: next,
1089 this_value: next_this,
1090 arguments: next_arguments,
1091 }) => {
1092 callee = next;
1093 this = next_this;
1094 args = Cow::Owned(next_arguments);
1095 new_target = Value::UNDEFINED;
1096 }
1097 Ok(BuiltinOutcome::ConstructCall { .. }) => {
1098 return InvokeOutcome::Threw(
1099 Value::UNDEFINED,
1100 ThrowOrigin::TypeError { operation: "call" },
1101 );
1102 }
1103 Ok(BuiltinOutcome::GeneratorNext {
1104 generator,
1105 resume_value,
1106 }) => {
1107 return self.resume_generator(generator, resume_value);
1108 }
1109 Err(EvalFailure::Throw(origin)) => {
1110 return InvokeOutcome::Threw(Value::UNDEFINED, origin);
1111 }
1112 Err(EvalFailure::ThrowValue(value)) => {
1113 return InvokeOutcome::Threw(value, ThrowOrigin::Bytecode);
1114 }
1115 Err(EvalFailure::ThrowValueOrigin { value, origin }) => {
1116 return InvokeOutcome::Threw(value, origin);
1117 }
1118 Err(EvalFailure::Runtime(kind)) => {
1119 self.pending_fatal_kind.set(Some(kind));
1120 return InvokeOutcome::Fatal;
1121 }
1122 }
1123 }
1124 Ok(CalleeKind::Bound) => {
1125 let bound = self
1126 .machine
1127 .borrow()
1128 .flatten_bound(callee, this, args.as_ref());
1129 match bound {
1130 Ok(bound) => {
1131 callee = bound.target;
1132 if new_target == Value::UNDEFINED {
1133 this = bound.this_value;
1134 }
1135 args = Cow::Owned(bound.arguments);
1136 }
1137 Err(kind) => {
1138 self.pending_fatal_kind.set(Some(kind));
1139 return InvokeOutcome::Fatal;
1140 }
1141 }
1142 }
1143 Ok(CalleeKind::NotCallable) => {
1144 return InvokeOutcome::Threw(
1145 Value::UNDEFINED,
1146 ThrowOrigin::TypeError { operation: "call" },
1147 );
1148 }
1149 Err(kind) => {
1150 self.pending_fatal_kind.set(Some(kind));
1151 return InvokeOutcome::Fatal;
1152 }
1153 }
1154 }
1155 }
1156
1157 fn invoke_runtime(
1160 &self,
1161 target: crate::RuntimeFunction,
1162 captures: &[Value],
1163 this: Value,
1164 new_target: Value,
1165 args: &[Value],
1166 ) -> InvokeOutcome {
1167 let index = target.function.get() as usize;
1168 let handle = self.code_ref(target.module);
1169 let flags = handle.code(target.module).functions()[index].flags();
1170 if flags.is_generator && !flags.is_async {
1171 let created = self.machine.borrow_mut().create_generator(GeneratorStart {
1172 target,
1173 captures: captures.to_vec(),
1174 this_value: this,
1175 new_target,
1176 args: args.to_vec(),
1177 });
1178 return match created {
1179 Ok(generator) => InvokeOutcome::Value(generator),
1180 Err(kind) => {
1181 self.pending_fatal_kind.set(Some(kind));
1182 InvokeOutcome::Fatal
1183 }
1184 };
1185 }
1186 if flags.is_async && !flags.is_generator {
1187 let outcome = self
1192 .machine
1193 .borrow_mut()
1194 .start_async_call(target, captures, this, new_target, args);
1195 return match outcome {
1196 Ok(promise) => InvokeOutcome::Value(promise),
1197 Err(failure) => self.failure_outcome(failure),
1198 };
1199 }
1200 if self.backend == Backend::Reference || self.is_dynamic_module(target.module) {
1201 return match self.execute(
1202 target.module,
1203 index,
1204 this,
1205 new_target,
1206 args.to_vec(),
1207 captures,
1208 ) {
1209 Ok((FrameCompletion::Normal(value), _)) => InvokeOutcome::Value(value),
1210 Ok((FrameCompletion::Unwind(value, origin, _), _)) => {
1211 InvokeOutcome::Threw(value, origin)
1212 }
1213 Ok((FrameCompletion::Suspend(value, _), _)) => {
1214 self.pending_fatal_kind
1215 .set(Some(RuntimeErrorKind::InvalidValue { value }));
1216 InvokeOutcome::Fatal
1217 }
1218 Err(error) => {
1219 self.pending_error.set(Some(error));
1220 InvokeOutcome::Fatal
1221 }
1222 };
1223 }
1224 self.invoke_linked(target, captures, this, new_target, args)
1225 }
1226
1227 fn resume_generator(&self, generator: Value, resume_value: Value) -> InvokeOutcome {
1228 let state = self.machine.borrow_mut().take_generator_state(generator);
1229 let resumed = match state {
1230 Ok(GeneratorState::Completed) => {
1231 return self.generator_result(Value::UNDEFINED, true);
1232 }
1233 Ok(GeneratorState::SuspendedStart(start)) => {
1234 if self.backend == Backend::Reference || self.is_dynamic_module(start.target.module)
1235 {
1236 self.start_reference_generator(start)
1237 } else {
1238 self.start_linked_generator(start)
1239 }
1240 }
1241 Ok(GeneratorState::Suspended(activation)) => {
1242 if self.backend == Backend::Reference
1243 || self.is_dynamic_module(activation.target.module)
1244 {
1245 self.resume_reference_generator(activation, resume_value)
1246 } else {
1247 self.resume_linked_generator(activation, resume_value)
1248 }
1249 }
1250 Ok(GeneratorState::Executing) => {
1251 unreachable!("executing state is rejected by take_generator_state")
1252 }
1253 Err(failure) => return self.failure_outcome(failure),
1254 };
1255
1256 match resumed {
1257 Some(GeneratorResume::Yield { value, activation }) => {
1258 let result = self
1259 .machine
1260 .borrow_mut()
1261 .settle_generator_yield(generator, value, activation);
1262 self.eval_outcome(result)
1263 }
1264 Some(GeneratorResume::Return(value)) => {
1265 let settled = self
1266 .machine
1267 .borrow_mut()
1268 .settle_generator_completed(generator);
1269 if let Err(failure) = settled {
1270 return self.failure_outcome(failure);
1271 }
1272 self.generator_result(value, true)
1273 }
1274 Some(GeneratorResume::Throw { value, origin, .. }) => {
1275 let settled = self
1276 .machine
1277 .borrow_mut()
1278 .settle_generator_completed(generator);
1279 if let Err(failure) = settled {
1280 return self.failure_outcome(failure);
1281 }
1282 InvokeOutcome::Threw(value, origin)
1283 }
1284 None => {
1285 if let Err(failure) = self
1286 .machine
1287 .borrow_mut()
1288 .settle_generator_completed(generator)
1289 {
1290 return self.failure_outcome(failure);
1291 }
1292 InvokeOutcome::Fatal
1293 }
1294 }
1295 }
1296
1297 fn generator_result(&self, value: Value, done: bool) -> InvokeOutcome {
1298 let result = self.machine.borrow_mut().iterator_result(value, done);
1299 self.eval_outcome(result)
1300 }
1301
1302 fn eval_outcome(&self, result: Result<Value, EvalFailure>) -> InvokeOutcome {
1303 match result {
1304 Ok(value) => InvokeOutcome::Value(value),
1305 Err(failure) => self.failure_outcome(failure),
1306 }
1307 }
1308
1309 fn failure_outcome(&self, failure: EvalFailure) -> InvokeOutcome {
1310 match failure {
1311 EvalFailure::Throw(origin) => InvokeOutcome::Threw(Value::UNDEFINED, origin),
1312 EvalFailure::ThrowValue(value) => InvokeOutcome::Threw(value, ThrowOrigin::Bytecode),
1313 EvalFailure::ThrowValueOrigin { value, origin } => InvokeOutcome::Threw(value, origin),
1314 EvalFailure::Runtime(kind) => {
1315 self.pending_fatal_kind.set(Some(kind));
1316 InvokeOutcome::Fatal
1317 }
1318 }
1319 }
1320
1321 fn get_outcome(
1322 &self,
1323 outcome: Result<GetOutcome, EvalFailure>,
1324 receiver: Value,
1325 ) -> InvokeOutcome {
1326 match outcome {
1327 Ok(GetOutcome::Value(value)) => InvokeOutcome::Value(value),
1328 Ok(GetOutcome::Text(text)) => {
1329 match self.machine.borrow_mut().allocate(HeapEntry::String(text)) {
1330 Ok(value) => InvokeOutcome::Value(value),
1331 Err(kind) => {
1332 self.pending_fatal_kind.set(Some(kind));
1333 InvokeOutcome::Fatal
1334 }
1335 }
1336 }
1337 Ok(GetOutcome::Getter(getter)) => {
1338 self.invoke_callee(getter, receiver, &[], Value::UNDEFINED)
1339 }
1340 Err(failure) => self.failure_outcome(failure),
1341 }
1342 }
1343
1344 fn get_ascii(&self, object: Value, name: &str) -> InvokeOutcome {
1345 let outcome = self.machine.borrow_mut().resolve_get_ascii(object, name);
1346 self.get_outcome(outcome, object)
1347 }
1348
1349 fn get_iterator_active(
1350 &self,
1351 source: Value,
1352 kind: bamts_bytecode::IteratorKind,
1353 ) -> InvokeOutcome {
1354 if kind == bamts_bytecode::IteratorKind::Keys {
1355 let created = self.machine.borrow_mut().create_iterator(source, kind);
1356 return self.eval_outcome(created);
1357 }
1358 let method = {
1359 let mut machine = self.machine.borrow_mut();
1360 let symbol = machine.intrinsics.builtins.symbol_iterator();
1361 let key = match machine.to_property_key(symbol) {
1362 Ok(key) => key,
1363 Err(failure) => return self.failure_outcome(failure),
1364 };
1365 machine.resolve_get(source, &key)
1366 };
1367 let method = match self.get_outcome(method, source) {
1368 InvokeOutcome::Value(method) => method,
1369 other => return other,
1370 };
1371 match self.machine.borrow().is_callable(method) {
1372 Ok(true) => {}
1373 Ok(false) => {
1374 return InvokeOutcome::Threw(
1375 Value::UNDEFINED,
1376 ThrowOrigin::TypeError {
1377 operation: "value is not iterable",
1378 },
1379 );
1380 }
1381 Err(failure) => return self.failure_outcome(failure),
1382 }
1383 let iterator = match self.invoke_callee(method, source, &[], Value::UNDEFINED) {
1384 InvokeOutcome::Value(iterator) => iterator,
1385 other => return other,
1386 };
1387 if !self.machine.borrow().is_object(iterator) {
1388 return InvokeOutcome::Threw(
1389 Value::UNDEFINED,
1390 ThrowOrigin::TypeError {
1391 operation: "iterator method returned a non-object",
1392 },
1393 );
1394 }
1395 let next = match self.get_ascii(iterator, "next") {
1396 InvokeOutcome::Value(next) => next,
1397 other => return other,
1398 };
1399 let created = self
1400 .machine
1401 .borrow_mut()
1402 .create_protocol_iterator(iterator, next);
1403 self.eval_outcome(created)
1404 }
1405
1406 fn iterator_next_active(&self, iterator: Value) -> Result<(bool, Value), InvokeOutcome> {
1407 let prepared = self.machine.borrow_mut().prepare_iterator_next(iterator);
1408 let result = match prepared {
1409 Ok(IteratorNextPrepared::Ready { done, value }) => return Ok((done, value)),
1410 Ok(IteratorNextPrepared::Call { callee, this_value }) => {
1411 match self.invoke_callee(callee, this_value, &[], Value::UNDEFINED) {
1412 InvokeOutcome::Value(result) => result,
1413 other => return Err(other),
1414 }
1415 }
1416 Err(failure) => return Err(self.failure_outcome(failure)),
1417 };
1418 if !self.machine.borrow().is_object(result) {
1419 return Err(InvokeOutcome::Threw(
1420 Value::UNDEFINED,
1421 ThrowOrigin::TypeError {
1422 operation: "iterator next returned a non-object",
1423 },
1424 ));
1425 }
1426 let done = match self.get_ascii(result, "done") {
1427 InvokeOutcome::Value(done) => done,
1428 other => return Err(other),
1429 };
1430 if self.machine.borrow().truthy(done) {
1431 return Ok((true, Value::UNDEFINED));
1432 }
1433 let value = match self.get_ascii(result, "value") {
1434 InvokeOutcome::Value(value) => value,
1435 other => return Err(other),
1436 };
1437 Ok((false, value))
1438 }
1439
1440 fn array_extend_active(&self, array: Value, iterable: Value) -> InvokeOutcome {
1441 let iterator = match self.get_iterator_active(iterable, bamts_bytecode::IteratorKind::Sync)
1442 {
1443 InvokeOutcome::Value(iterator) => iterator,
1444 other => return other,
1445 };
1446 loop {
1447 let (done, value) = match self.iterator_next_active(iterator) {
1448 Ok(step) => step,
1449 Err(outcome) => return outcome,
1450 };
1451 if done {
1452 return InvokeOutcome::Value(Value::UNDEFINED);
1453 }
1454 if let Err(failure) = self.machine.borrow_mut().array_push(array, value) {
1455 return self.failure_outcome(failure);
1456 }
1457 }
1458 }
1459
1460 fn start_reference_generator(&self, start: GeneratorStart) -> Option<GeneratorResume> {
1461 let target = start.target;
1462 let index = target.function.get() as usize;
1463 let handle = self.code_ref(target.module);
1464 let code = handle.code(target.module);
1465 let register_count = code.functions()[index].register_count() as usize;
1466 let mut registers = self.seed_registers(code, index, &start.captures, &start.args);
1467 if let Err(kind) = self
1468 .machine
1469 .borrow_mut()
1470 .reserve_suspended_activation_registers(register_count)
1471 {
1472 self.pending_fatal_kind.set(Some(kind));
1473 return None;
1474 }
1475 if let Err(kind) = self.machine.borrow_mut().enter_native_generator() {
1476 self.machine
1477 .borrow_mut()
1478 .release_suspended_activation_registers(register_count);
1479 self.pending_fatal_kind.set(Some(kind));
1480 return None;
1481 }
1482 self.activations.borrow_mut().push(Activation {
1483 this_value: start.this_value,
1484 new_target: start.new_target,
1485 args: start.args.clone(),
1486 arguments_object: None,
1487 pending_resume: None,
1488 });
1489 let completion = self.run_frame(
1490 target.module,
1491 index,
1492 code,
1493 &mut registers,
1494 FrameDrive::GeneratorStart,
1495 );
1496 let activation = self
1497 .activations
1498 .borrow_mut()
1499 .pop()
1500 .expect("generator activation exists");
1501 self.machine.borrow_mut().leave_native_generator();
1502 self.finish_reference_generator(target, registers, activation, completion)
1503 }
1504
1505 fn resume_reference_generator(
1506 &self,
1507 mut suspended: SuspendedActivation,
1508 sent: Value,
1509 ) -> Option<GeneratorResume> {
1510 let target = suspended.target;
1511 let index = target.function.get() as usize;
1512 let handle = self.code_ref(target.module);
1513 let code = handle.code(target.module);
1514 let register_count = suspended.registers.len();
1515 if let Err(kind) = self.machine.borrow_mut().enter_native_generator() {
1516 self.machine
1517 .borrow_mut()
1518 .release_suspended_activation_registers(register_count);
1519 self.pending_fatal_kind.set(Some(kind));
1520 return None;
1521 }
1522 self.activations.borrow_mut().push(Activation {
1523 this_value: suspended.this_value,
1524 new_target: suspended.new_target,
1525 args: suspended.args.clone(),
1526 arguments_object: suspended.arguments_object,
1527 pending_resume: None,
1528 });
1529 let completion = self.run_frame(
1530 target.module,
1531 index,
1532 code,
1533 &mut suspended.registers,
1534 FrameDrive::GeneratorResume {
1535 token: suspended.resume_token,
1536 sent,
1537 },
1538 );
1539 let activation = self
1540 .activations
1541 .borrow_mut()
1542 .pop()
1543 .expect("generator activation exists");
1544 self.machine.borrow_mut().leave_native_generator();
1545 self.finish_reference_generator(target, suspended.registers, activation, completion)
1546 }
1547
1548 fn finish_reference_generator(
1549 &self,
1550 target: crate::RuntimeFunction,
1551 registers: Vec<Value>,
1552 activation: Activation,
1553 completion: Result<FrameCompletion, RuntimeError>,
1554 ) -> Option<GeneratorResume> {
1555 let register_count = registers.len();
1556 match completion {
1557 Ok(FrameCompletion::Suspend(value, resume_token)) => Some(GeneratorResume::Yield {
1558 value,
1559 activation: SuspendedActivation {
1560 target,
1561 registers,
1562 this_value: activation.this_value,
1563 new_target: activation.new_target,
1564 args: activation.args,
1565 arguments_object: activation.arguments_object,
1566 resume_token,
1567 },
1568 }),
1569 Ok(FrameCompletion::Normal(value)) => {
1570 self.machine
1571 .borrow_mut()
1572 .release_suspended_activation_registers(register_count);
1573 Some(GeneratorResume::Return(value))
1574 }
1575 Ok(FrameCompletion::Unwind(value, origin, _)) => {
1576 self.machine
1577 .borrow_mut()
1578 .release_suspended_activation_registers(register_count);
1579 Some(GeneratorResume::Throw { value, origin })
1580 }
1581 Err(error) => {
1582 self.machine
1583 .borrow_mut()
1584 .release_suspended_activation_registers(register_count);
1585 self.pending_error.set(Some(error));
1586 None
1587 }
1588 }
1589 }
1590
1591 fn start_linked_generator(&self, start: GeneratorStart) -> Option<GeneratorResume> {
1592 let index = start.target.function.get() as usize;
1593 let handle = self.code_ref(start.target.module);
1594 let code = handle.code(start.target.module);
1595 let registers = self.seed_registers(code, index, &start.captures, &start.args);
1596 if let Err(kind) = self
1597 .machine
1598 .borrow_mut()
1599 .reserve_suspended_activation_registers(registers.len())
1600 {
1601 self.pending_fatal_kind.set(Some(kind));
1602 return None;
1603 }
1604 self.drive_linked_generator(
1605 SuspendedActivation {
1606 target: start.target,
1607 registers,
1608 this_value: start.this_value,
1609 new_target: start.new_target,
1610 args: start.args,
1611 arguments_object: None,
1612 resume_token: 0,
1613 },
1614 None,
1615 )
1616 }
1617
1618 fn resume_linked_generator(
1619 &self,
1620 activation: SuspendedActivation,
1621 sent: Value,
1622 ) -> Option<GeneratorResume> {
1623 self.drive_linked_generator(activation, Some(sent))
1624 }
1625
1626 fn drive_linked_generator(
1627 &self,
1628 mut suspended: SuspendedActivation,
1629 pending_resume: Option<Value>,
1630 ) -> Option<GeneratorResume> {
1631 let register_count = suspended.registers.len();
1632 if let Err(kind) = self.machine.borrow_mut().enter_native_generator() {
1633 self.machine
1634 .borrow_mut()
1635 .release_suspended_activation_registers(register_count);
1636 self.pending_fatal_kind.set(Some(kind));
1637 return None;
1638 }
1639 let length = match u16::try_from(register_count) {
1640 Ok(length) => length,
1641 Err(_) => {
1642 self.machine.borrow_mut().leave_native_generator();
1643 self.machine
1644 .borrow_mut()
1645 .release_suspended_activation_registers(register_count);
1646 self.pending_fatal_kind
1647 .set(Some(RuntimeErrorKind::RegisterLimitExceeded {
1648 limit: self.max_total_registers(),
1649 }));
1650 return None;
1651 }
1652 };
1653 self.activations.borrow_mut().push(Activation {
1654 this_value: suspended.this_value,
1655 new_target: suspended.new_target,
1656 args: suspended.args.clone(),
1657 arguments_object: suspended.arguments_object,
1658 pending_resume,
1659 });
1660 let handles = suspended.registers.as_mut_ptr();
1661 let (invoked, next_token, out) = {
1662 let mut shadow = ShadowFrame::new(
1663 std::ptr::null_mut(),
1664 suspended.resume_token,
1665 suspended.target.module.get(),
1666 handles,
1667 length,
1668 );
1669 let mut out = Completion::new(Value::UNDEFINED);
1670 let invoked = self.entries.invoke(
1671 suspended.target.module.get(),
1672 suspended.target.function.get(),
1673 &mut shadow,
1674 &mut out,
1675 );
1676 (invoked, shadow.bytecode_pc, out)
1677 };
1678 let activation = self
1679 .activations
1680 .borrow_mut()
1681 .pop()
1682 .expect("generator activation exists");
1683 self.machine.borrow_mut().leave_native_generator();
1684 suspended.arguments_object = activation.arguments_object;
1685
1686 match invoked {
1687 Ok(CompletionTag::Suspend) if next_token != 0 => {
1688 suspended.resume_token = next_token;
1689 Some(GeneratorResume::Yield {
1690 value: out.value,
1691 activation: suspended,
1692 })
1693 }
1694 Ok(CompletionTag::Normal) => {
1695 self.pending_throw.take();
1696 self.machine
1697 .borrow_mut()
1698 .release_suspended_activation_registers(register_count);
1699 Some(GeneratorResume::Return(out.value))
1700 }
1701 Ok(CompletionTag::Throw) => {
1702 self.machine
1703 .borrow_mut()
1704 .release_suspended_activation_registers(register_count);
1705 let (value, origin) = self.take_matching_throw(out.value);
1706 Some(GeneratorResume::Throw { value, origin })
1707 }
1708 Ok(CompletionTag::Suspend | CompletionTag::FatalTrap) => {
1709 self.machine
1710 .borrow_mut()
1711 .release_suspended_activation_registers(register_count);
1712 None
1713 }
1714 Err(error) => {
1715 self.machine
1716 .borrow_mut()
1717 .release_suspended_activation_registers(register_count);
1718 self.pending_abi_error.set(Some(error));
1719 None
1720 }
1721 }
1722 }
1723
1724 fn invoke_linked(
1727 &self,
1728 target: crate::RuntimeFunction,
1729 captures: &[Value],
1730 this: Value,
1731 new_target: Value,
1732 args: &[Value],
1733 ) -> InvokeOutcome {
1734 debug_assert!(!self.is_dynamic_module(target.module));
1735 let index = target.function.get() as usize;
1736 let handle = self.code_ref(target.module);
1737 let code = handle.code(target.module);
1738 let register_count = code.functions()[index].register_count() as usize;
1739 let mut registers = self.seed_registers(code, index, captures, args);
1740 let length = match u16::try_from(registers.len()) {
1741 Ok(length) => length,
1742 Err(_) => {
1743 let limit = self.max_total_registers();
1744 self.pending_fatal_kind
1745 .set(Some(RuntimeErrorKind::RegisterLimitExceeded { limit }));
1746 return InvokeOutcome::Fatal;
1747 }
1748 };
1749 if let Err(kind) = self
1750 .machine
1751 .borrow_mut()
1752 .reserve_native_activation(register_count)
1753 {
1754 self.pending_fatal_kind.set(Some(kind));
1755 return InvokeOutcome::Fatal;
1756 }
1757 self.activations.borrow_mut().push(Activation {
1758 this_value: this,
1759 new_target,
1760 args: args.to_vec(),
1761 arguments_object: None,
1762 pending_resume: None,
1763 });
1764 let handles = registers.as_mut_ptr();
1765 let (tag, out) = {
1766 let mut shadow = ShadowFrame::new(
1767 std::ptr::null_mut(),
1768 0,
1769 target.module.get(),
1770 handles,
1771 length,
1772 );
1773 let mut out = Completion::new(Value::UNDEFINED);
1774 let tag = self.entries.invoke(
1775 target.module.get(),
1776 target.function.get(),
1777 &mut shadow,
1778 &mut out,
1779 );
1780 (tag, out)
1781 };
1782 drop(registers);
1783 self.activations.borrow_mut().pop();
1784 self.machine
1785 .borrow_mut()
1786 .release_native_activation(register_count);
1787 match tag {
1788 Ok(CompletionTag::Normal) => {
1789 self.pending_throw.take();
1790 InvokeOutcome::Value(out.value)
1791 }
1792 Ok(CompletionTag::Throw) => {
1793 let (value, origin) = self.take_matching_throw(out.value);
1794 InvokeOutcome::Threw(value, origin)
1795 }
1796 Ok(CompletionTag::Suspend | CompletionTag::FatalTrap) => InvokeOutcome::Fatal,
1797 Err(error) => {
1798 self.pending_abi_error.set(Some(error));
1799 InvokeOutcome::Fatal
1800 }
1801 }
1802 }
1803
1804 fn evaluate_import(&self, module: ModuleId) -> Result<(), ImportFailure> {
1805 let begun = self.machine.borrow_mut().begin_module_evaluation(module);
1806 let dependencies = match begun {
1807 Err(error) => {
1808 self.pending_error.set(Some(error));
1809 return Err(ImportFailure::Fatal);
1810 }
1811 Ok(crate::ModuleEvaluation::Cycle) => return Ok(()),
1812 Ok(crate::ModuleEvaluation::Evaluated(Ok(()))) => return Ok(()),
1813 Ok(crate::ModuleEvaluation::Evaluated(Err(error))) => {
1814 if matches!(error.kind, RuntimeErrorKind::UncaughtThrow { .. }) {
1815 return Err(ImportFailure::Threw(error));
1816 }
1817 self.pending_error.set(Some(error));
1818 return Err(ImportFailure::Fatal);
1819 }
1820 Ok(crate::ModuleEvaluation::Ready(dependencies)) => dependencies,
1821 };
1822 for dependency in dependencies {
1823 if let Err(failure) = self.evaluate_import(dependency) {
1824 let mut machine = self.machine.borrow_mut();
1825 match &failure {
1826 ImportFailure::Threw(error) => {
1827 machine.settle_module_evaluation(module, Err(error.clone()));
1828 }
1829 ImportFailure::Fatal => machine.abort_module_evaluation(module),
1830 }
1831 return Err(failure);
1832 }
1833 }
1834
1835 let function = self.module(module).entry();
1836 let outcome = self.invoke_runtime(
1837 crate::RuntimeFunction { module, function },
1838 &[],
1839 Value::UNDEFINED,
1840 Value::UNDEFINED,
1841 &[],
1842 );
1843 match outcome {
1844 InvokeOutcome::Value(_) => {
1845 self.machine
1846 .borrow_mut()
1847 .settle_module_evaluation(module, Ok(()));
1848 Ok(())
1849 }
1850 InvokeOutcome::Threw(value, origin) => {
1851 let error = self.error_at(
1852 module,
1853 RuntimeErrorKind::UncaughtThrow { value, origin },
1854 function.get() as usize,
1855 0,
1856 );
1857 self.machine
1858 .borrow_mut()
1859 .settle_module_evaluation(module, Err(error.clone()));
1860 Err(ImportFailure::Threw(error))
1861 }
1862 InvokeOutcome::Fatal => {
1863 self.machine.borrow_mut().abort_module_evaluation(module);
1864 Err(ImportFailure::Fatal)
1865 }
1866 }
1867 }
1868
1869 fn import_namespace(&self, requester: ModuleId, specifier: u32) -> HelperResult {
1870 let target = self
1871 .machine
1872 .borrow()
1873 .resolve_import(requester, ConstantId::new(specifier));
1874 let target = match target {
1875 Ok(target) => target,
1876 Err(kind) => return self.fatal(kind),
1877 };
1878 if let crate::ImportTarget::Local(module) = target
1879 && let Err(failure) = self.evaluate_import(module)
1880 {
1881 return match failure {
1882 ImportFailure::Threw(error) => self.fail(crate::import_failure(&error)),
1883 ImportFailure::Fatal => HelperResult {
1884 tag: CompletionTag::FatalTrap,
1885 value: Value::UNDEFINED,
1886 },
1887 };
1888 }
1889 let namespace = self
1890 .machine
1891 .borrow_mut()
1892 .imported_namespace(requester, target);
1893 match namespace {
1894 Ok(value) => HelperResult::normal(value),
1895 Err(kind) => self.fatal(kind),
1896 }
1897 }
1898
1899 fn fatal(&self, kind: RuntimeErrorKind) -> HelperResult {
1902 self.pending_fatal_kind.set(Some(kind));
1903 HelperResult {
1904 tag: CompletionTag::FatalTrap,
1905 value: Value::UNDEFINED,
1906 }
1907 }
1908
1909 fn fail(&self, failure: EvalFailure) -> HelperResult {
1910 match failure {
1911 EvalFailure::Throw(origin) => {
1912 self.pending_throw.set(Some(PendingThrow {
1913 value: Value::UNDEFINED,
1914 origin,
1915 }));
1916 HelperResult::throw(Value::UNDEFINED)
1917 }
1918 EvalFailure::ThrowValue(value) => {
1919 self.pending_throw.set(Some(PendingThrow {
1920 value,
1921 origin: ThrowOrigin::Bytecode,
1922 }));
1923 HelperResult::throw(value)
1924 }
1925 EvalFailure::ThrowValueOrigin { value, origin } => {
1926 self.pending_throw.set(Some(PendingThrow { value, origin }));
1927 HelperResult::throw(value)
1928 }
1929 EvalFailure::Runtime(kind) => self.fatal(kind),
1930 }
1931 }
1932
1933 fn eval_result(&self, result: Result<Value, EvalFailure>) -> HelperResult {
1934 match result {
1935 Ok(value) => HelperResult::normal(value),
1936 Err(failure) => self.fail(failure),
1937 }
1938 }
1939
1940 fn outcome_result(&self, outcome: InvokeOutcome) -> HelperResult {
1941 match outcome {
1942 InvokeOutcome::Value(value) => HelperResult::normal(value),
1943 InvokeOutcome::Threw(value, origin) => {
1944 self.pending_throw.set(Some(PendingThrow { value, origin }));
1945 HelperResult::throw(value)
1946 }
1947 InvokeOutcome::Fatal => HelperResult {
1948 tag: CompletionTag::FatalTrap,
1949 value: Value::UNDEFINED,
1950 },
1951 }
1952 }
1953
1954 fn validated(&self, value: Value) -> HelperResult {
1955 HelperResult::normal(value)
1956 }
1957
1958 fn allocated(&self, entry: HeapEntry) -> HelperResult {
1959 let result = self.machine.borrow_mut().allocate(entry);
1960 match result {
1961 Ok(value) => HelperResult::normal(value),
1962 Err(kind) => self.fatal(kind),
1963 }
1964 }
1965
1966 fn constant_text(&self, module: ModuleId, id: u32) -> EcmaString {
1967 self.machine
1968 .borrow()
1969 .constant_text(module, ConstantId::new(id))
1970 .clone()
1971 }
1972
1973 fn load_arguments(&self) -> HelperResult {
1975 let args = match self.activations.borrow().last() {
1976 Some(activation) => activation.args.clone(),
1977 None => return HelperResult::normal(Value::UNDEFINED),
1978 };
1979 if let Some(existing) = self
1980 .activations
1981 .borrow()
1982 .last()
1983 .and_then(|activation| activation.arguments_object)
1984 {
1985 return HelperResult::normal(existing);
1986 }
1987 let prototype = self.machine.borrow().intrinsics.array_prototype;
1988 let allocated = self.machine.borrow_mut().allocate(HeapEntry::Array {
1989 elements: args,
1990 properties: PropertyMap::default(),
1991 prototype: Some(prototype),
1992 extensible: true,
1993 length_writable: true,
1994 });
1995 let value = match allocated {
1996 Ok(value) => value,
1997 Err(kind) => return self.fatal(kind),
1998 };
1999 if let Some(activation) = self.activations.borrow_mut().last_mut() {
2000 activation.arguments_object = Some(value);
2001 }
2002 HelperResult::normal(value)
2003 }
2004
2005 fn construct(&self, callee: Value, arguments: &[Value]) -> HelperResult {
2009 let mut callee = callee;
2010 let mut arguments = Cow::Borrowed(arguments);
2011 if matches!(
2012 self.machine.borrow().callee_kind(callee),
2013 Ok(CalleeKind::Bound)
2014 ) {
2015 let bound =
2016 self.machine
2017 .borrow()
2018 .flatten_bound(callee, Value::UNDEFINED, arguments.as_ref());
2019 match bound {
2020 Ok(bound) => {
2021 callee = bound.target;
2022 arguments = Cow::Owned(bound.arguments);
2023 }
2024 Err(kind) => return self.fatal(kind),
2025 }
2026 }
2027 let kind = self.machine.borrow().callee_kind(callee);
2028 match kind {
2029 Ok(CalleeKind::Builtin { id }) => {
2030 let result = self.machine.borrow_mut().call_builtin(
2031 id,
2032 Value::UNDEFINED,
2033 arguments.as_ref(),
2034 true,
2035 );
2036 match result {
2037 Ok(BuiltinOutcome::Value(value)) => HelperResult::normal(value),
2038 Ok(BuiltinOutcome::Call { .. } | BuiltinOutcome::GeneratorNext { .. }) => {
2039 self.pending_throw.set(Some(PendingThrow {
2040 value: Value::UNDEFINED,
2041 origin: ThrowOrigin::TypeError {
2042 operation: "construct",
2043 },
2044 }));
2045 HelperResult::throw(Value::UNDEFINED)
2046 }
2047 Ok(BuiltinOutcome::ConstructCall {
2048 callee: continuation,
2049 this_value,
2050 arguments: continuation_arguments,
2051 prototype,
2052 }) => {
2053 let instance = match self
2054 .machine
2055 .borrow_mut()
2056 .allocate_constructed_receiver_with(prototype)
2057 {
2058 Ok(value) => value,
2059 Err(kind) => return self.fatal(kind),
2060 };
2061 let outcome = self.invoke_callee(
2062 continuation,
2063 this_value,
2064 &continuation_arguments,
2065 callee,
2066 );
2067 match outcome {
2068 InvokeOutcome::Value(returned) => {
2069 let is_object = self.machine.borrow().is_object(returned);
2070 HelperResult::normal(if is_object { returned } else { instance })
2071 }
2072 other => self.outcome_result(other),
2073 }
2074 }
2075 Err(failure) => self.fail(failure),
2076 }
2077 }
2078 Ok(CalleeKind::Runtime { target, captures }) => {
2079 let flags = {
2080 let handle = self.code_ref(target.module);
2081 handle.code(target.module).functions()[target.function.get() as usize].flags()
2082 };
2083 if flags.is_async && !flags.is_generator {
2084 self.pending_throw.set(Some(PendingThrow {
2085 value: Value::UNDEFINED,
2086 origin: ThrowOrigin::TypeError {
2087 operation: "construct",
2088 },
2089 }));
2090 return HelperResult::throw(Value::UNDEFINED);
2091 }
2092 let instance = {
2093 let allocated = self
2094 .machine
2095 .borrow_mut()
2096 .allocate_constructed_receiver(callee);
2097 match allocated {
2098 Ok(value) => value,
2099 Err(kind) => return self.fatal(kind),
2100 }
2101 };
2102 let outcome =
2103 self.invoke_runtime(target, &captures, instance, callee, arguments.as_ref());
2104 match outcome {
2105 InvokeOutcome::Value(returned) => {
2106 let is_object = self.machine.borrow().is_object(returned);
2107 HelperResult::normal(if is_object { returned } else { instance })
2108 }
2109 other => self.outcome_result(other),
2110 }
2111 }
2112 Ok(CalleeKind::Bound) => self.fatal(RuntimeErrorKind::InvalidValue { value: callee }),
2113 Ok(CalleeKind::NotCallable) => {
2114 self.pending_throw.set(Some(PendingThrow {
2115 value: Value::UNDEFINED,
2116 origin: ThrowOrigin::TypeError {
2117 operation: "construct",
2118 },
2119 }));
2120 HelperResult::throw(Value::UNDEFINED)
2121 }
2122 Err(kind) => self.fatal(kind),
2123 }
2124 }
2125
2126 fn run_linked(&mut self) -> Result<ExecutionOutcome, NativeError> {
2129 self.machine.borrow_mut().instantiate_modules()?;
2130 let module = self.program.entry();
2131 let execution = self.evaluate_linked_module(module)?.ok_or_else(|| {
2132 let function = self.module(module).entry().get() as usize;
2133 NativeError::Runtime(self.error_at(
2134 module,
2135 RuntimeErrorKind::InvalidVerifiedProgram {
2136 module,
2137 instruction: Instruction::Halt,
2138 },
2139 function,
2140 0,
2141 ))
2142 })?;
2143 self.machine
2147 .borrow_mut()
2148 .run_to_quiescence()
2149 .map_err(NativeError::Runtime)?;
2150 Ok(execution.outcome)
2151 }
2152
2153 fn evaluate_linked_module(
2154 &mut self,
2155 module: ModuleId,
2156 ) -> Result<Option<Execution>, NativeError> {
2157 let dependencies = match self.machine.borrow_mut().begin_module_evaluation(module)? {
2158 crate::ModuleEvaluation::Cycle => return Ok(None),
2159 crate::ModuleEvaluation::Evaluated(result) => {
2160 return result.map(|()| None).map_err(Into::into);
2161 }
2162 crate::ModuleEvaluation::Ready(dependencies) => dependencies,
2163 };
2164 for dependency in dependencies {
2165 match self.evaluate_linked_module(dependency) {
2166 Ok(_) => {}
2167 Err(NativeError::Runtime(error)) => {
2168 let error = self
2169 .machine
2170 .borrow_mut()
2171 .finish_module_evaluation(module, Err(error))
2172 .expect_err("dependency failure remains an error");
2173 return Err(NativeError::Runtime(error));
2174 }
2175 Err(error) => {
2176 self.machine.borrow_mut().abort_module_evaluation(module);
2177 return Err(error);
2178 }
2179 }
2180 }
2181
2182 match self.invoke_linked_entry(module) {
2183 Ok(execution) => self
2184 .machine
2185 .borrow_mut()
2186 .finish_module_evaluation(module, Ok(execution))
2187 .map(Some)
2188 .map_err(Into::into),
2189 Err(NativeError::Runtime(error)) => {
2190 let error = self
2191 .machine
2192 .borrow_mut()
2193 .finish_module_evaluation(module, Err(error))
2194 .expect_err("module failure remains an error");
2195 Err(NativeError::Runtime(error))
2196 }
2197 Err(error) => {
2198 self.machine.borrow_mut().abort_module_evaluation(module);
2199 Err(error)
2200 }
2201 }
2202 }
2203
2204 fn invoke_linked_entry(&mut self, module: ModuleId) -> Result<Execution, NativeError> {
2205 let code = self.module(module);
2206 let function_id = code.entry();
2207 let function = function_id.get() as usize;
2208 let register_count = code.functions()[function].register_count() as usize;
2209 if self.max_call_depth() < 1 {
2210 return Err(NativeError::Runtime(self.error_at(
2211 module,
2212 RuntimeErrorKind::CallDepthExceeded {
2213 limit: self.max_call_depth(),
2214 },
2215 function,
2216 0,
2217 )));
2218 }
2219 if register_count > self.max_total_registers() {
2220 return Err(NativeError::Runtime(self.error_at(
2221 module,
2222 RuntimeErrorKind::RegisterLimitExceeded {
2223 limit: self.max_total_registers(),
2224 },
2225 function,
2226 0,
2227 )));
2228 }
2229 let mut registers = self.seed_registers(code, function, &[], &[]);
2230 let length = u16::try_from(register_count).map_err(|_| {
2231 NativeError::Runtime(self.error_at(
2232 module,
2233 RuntimeErrorKind::RegisterLimitExceeded {
2234 limit: self.max_total_registers(),
2235 },
2236 function,
2237 0,
2238 ))
2239 })?;
2240 let reserved = self
2241 .machine
2242 .borrow_mut()
2243 .reserve_native_activation(register_count);
2244 reserved.map_err(|kind| NativeError::Runtime(self.error_at(module, kind, function, 0)))?;
2245 self.activations.borrow_mut().push(Activation {
2246 this_value: Value::UNDEFINED,
2247 new_target: Value::UNDEFINED,
2248 args: Vec::new(),
2249 arguments_object: None,
2250 pending_resume: None,
2251 });
2252 let handles = registers.as_mut_ptr();
2253 let entries = self.entries;
2254 let (tag, out, fault_pc) = {
2255 let mut shadow =
2256 ShadowFrame::new(std::ptr::null_mut(), 0, module.get(), handles, length);
2257 let mut out = Completion::new(Value::UNDEFINED);
2258 let tag = with_native_ops(self, || {
2259 entries.invoke(module.get(), function_id.get(), &mut shadow, &mut out)
2260 });
2261 (tag, out, shadow.bytecode_pc as usize)
2262 };
2263 self.activations.borrow_mut().pop();
2264 self.machine
2265 .borrow_mut()
2266 .release_native_activation(register_count);
2267 match tag {
2268 Ok(CompletionTag::Normal) => {
2269 self.pending_throw.take();
2270 Ok(Execution {
2271 outcome: ExecutionOutcome {
2272 stdout: self.stdout.borrow().clone(),
2273 exit_code: self.exit_code.get(),
2274 },
2275 value: out.value,
2276 link: out.value,
2277 entry_registers: registers,
2278 })
2279 }
2280 Ok(CompletionTag::Throw) => {
2281 let (value, origin) = self.take_matching_throw(out.value);
2282 Err(NativeError::Runtime(self.error_at(
2283 module,
2284 RuntimeErrorKind::UncaughtThrow { value, origin },
2285 function,
2286 fault_pc,
2287 )))
2288 }
2289 Ok(CompletionTag::Suspend | CompletionTag::FatalTrap) => {
2290 if let Some(error) = self.pending_abi_error.take() {
2291 Err(NativeError::Abi(error))
2292 } else if let Some(error) = self.pending_error.take() {
2293 Err(NativeError::Runtime(error))
2294 } else if let Some(kind) = self.pending_fatal_kind.take() {
2295 Err(NativeError::Runtime(
2296 self.error_at(module, kind, function, fault_pc),
2297 ))
2298 } else {
2299 Err(NativeError::FatalTrap { value: out.value })
2300 }
2301 }
2302 Err(error) => Err(NativeError::Abi(error)),
2303 }
2304 }
2305}
2306
2307impl<'m, 'h, H: Host> NativeOps for NativeEngine<'m, 'h, H> {
2310 fn truthy(&self, _frame: &mut NativeFrame<'_>, value: Value) -> bool {
2311 self.machine.borrow().truthy(value)
2312 }
2313
2314 fn dispatch(&self, frame: &mut NativeFrame<'_>, call: HelperCall) -> HelperResult {
2315 let module = ModuleId::new(frame.module_id());
2316 let amount = match call {
2317 HelperCall::ResumeValue => None,
2318 HelperCall::ConsumeFuel { amount } => Some(u64::from(amount)),
2319 _ => Some(1),
2320 };
2321 if let Some(amount) = amount
2322 && let Err(kind) = self.machine.borrow_mut().consume_fuel(amount)
2323 {
2324 return self.fatal(kind);
2325 }
2326 match call {
2327 HelperCall::LoadConstant { const_id } => {
2328 let result = self
2329 .machine
2330 .borrow_mut()
2331 .load_constant_value(module, ConstantId::new(const_id));
2332 match result {
2333 Ok(value) => HelperResult::normal(value),
2334 Err(kind) => self.fatal(kind),
2335 }
2336 }
2337 HelperCall::Unary { op, operand } => match unary_from_selector(op) {
2338 Some(op) => {
2339 let result = self.machine.borrow_mut().eval_unary(op, operand);
2340 self.eval_result(result)
2341 }
2342 None => self.fatal(RuntimeErrorKind::InvalidValue {
2343 value: Value::UNDEFINED,
2344 }),
2345 },
2346 HelperCall::Binary { op, left, right } => match binary_from_selector(op) {
2347 Some(op) => {
2348 let result = self.machine.borrow_mut().eval_binary(op, left, right);
2349 self.eval_result(result)
2350 }
2351 None => self.fatal(RuntimeErrorKind::InvalidValue {
2352 value: Value::UNDEFINED,
2353 }),
2354 },
2355 HelperCall::CreateObject => {
2356 let prototype = self.machine.borrow().intrinsics.object_prototype;
2357 self.allocated(HeapEntry::Object {
2358 properties: PropertyMap::default(),
2359 prototype: Some(prototype),
2360 boxed_primitive: None,
2361 extensible: true,
2362 })
2363 }
2364 HelperCall::CreateArray => {
2365 let prototype = self.machine.borrow().intrinsics.array_prototype;
2366 self.allocated(HeapEntry::Array {
2367 elements: Vec::new(),
2368 properties: PropertyMap::default(),
2369 prototype: Some(prototype),
2370 extensible: true,
2371 length_writable: true,
2372 })
2373 }
2374 HelperCall::CreateCell => {
2375 let prototype = self.machine.borrow().intrinsics.array_prototype;
2376 self.allocated(HeapEntry::Array {
2377 elements: vec![Value::UNINITIALIZED],
2378 properties: PropertyMap::default(),
2379 prototype: Some(prototype),
2380 extensible: true,
2381 length_writable: true,
2382 })
2383 }
2384 HelperCall::CreateClosure {
2385 function_id,
2386 captures,
2387 } => {
2388 let function = FunctionId::new(function_id);
2389 let materialized = self
2390 .machine
2391 .borrow()
2392 .captures_from_array(module, captures, function);
2393 match materialized {
2394 Ok(captures) => {
2395 let prototype = self.machine.borrow().intrinsics.function_prototype;
2396 self.allocated(HeapEntry::Function {
2397 module,
2398 function,
2399 captures,
2400 properties: PropertyMap::default(),
2401 prototype: Some(prototype),
2402 extensible: true,
2403 })
2404 }
2405 Err(failure) => self.fail(failure),
2406 }
2407 }
2408 HelperCall::GetProperty { object, key } => {
2409 let key = {
2410 let coerced = self.machine.borrow().to_property_key(key);
2411 match coerced {
2412 Ok(key) => key,
2413 Err(failure) => return self.fail(failure),
2414 }
2415 };
2416 let outcome = self.machine.borrow_mut().resolve_get(object, &key);
2417 match outcome {
2418 Ok(GetOutcome::Value(value)) => self.validated(value),
2419 Ok(GetOutcome::Text(text)) => self.allocated(HeapEntry::String(text)),
2420 Ok(GetOutcome::Getter(getter)) => {
2421 let outcome = self.invoke_callee(getter, object, &[], Value::UNDEFINED);
2422 self.outcome_result(outcome)
2423 }
2424 Err(failure) => self.fail(failure),
2425 }
2426 }
2427 HelperCall::SetProperty { object, key, value } => {
2428 let key = {
2429 let coerced = self.machine.borrow().to_property_key(key);
2430 match coerced {
2431 Ok(key) => key,
2432 Err(failure) => return self.fail(failure),
2433 }
2434 };
2435 let outcome = self.machine.borrow_mut().resolve_set(object, key, value);
2436 match outcome {
2437 Ok(SetOutcome::Done) => HelperResult::normal(Value::UNDEFINED),
2438 Ok(SetOutcome::Setter(setter)) => {
2439 let outcome =
2440 self.invoke_callee(setter, object, &[value], Value::UNDEFINED);
2441 match outcome {
2442 InvokeOutcome::Value(_) => HelperResult::normal(Value::UNDEFINED),
2443 other => self.outcome_result(other),
2444 }
2445 }
2446 Err(failure) => self.fail(failure),
2447 }
2448 }
2449 HelperCall::DeleteProperty { object, key } => {
2450 let key = {
2451 let coerced = self.machine.borrow().to_property_key(key);
2452 match coerced {
2453 Ok(key) => key,
2454 Err(failure) => return self.fail(failure),
2455 }
2456 };
2457 let deleted = self.machine.borrow_mut().delete_property(object, &key);
2458 match deleted {
2459 Ok(deleted) => HelperResult::normal(Value::boolean(deleted)),
2460 Err(failure) => self.fail(failure),
2461 }
2462 }
2463 HelperCall::DefineAccessor {
2464 object,
2465 key,
2466 accessor,
2467 kind,
2468 } => {
2469 let kind = match accessor_from_selector(kind) {
2470 Some(kind) => kind,
2471 None => {
2472 return self.fatal(RuntimeErrorKind::InvalidValue {
2473 value: Value::UNDEFINED,
2474 });
2475 }
2476 };
2477 let key = {
2478 let coerced = self.machine.borrow().to_property_key(key);
2479 match coerced {
2480 Ok(key) => key,
2481 Err(failure) => return self.fail(failure),
2482 }
2483 };
2484 let defined = self
2485 .machine
2486 .borrow_mut()
2487 .define_accessor(object, key, accessor, kind);
2488 match defined {
2489 Ok(()) => HelperResult::normal(Value::UNDEFINED),
2490 Err(failure) => self.fail(failure),
2491 }
2492 }
2493 HelperCall::Call {
2494 callee,
2495 this_value,
2496 arguments,
2497 } => {
2498 let arguments = {
2499 let read = self.machine.borrow().arguments_from_array(arguments);
2500 match read {
2501 Ok(arguments) => arguments,
2502 Err(failure) => return self.fail(failure),
2503 }
2504 };
2505 let outcome = self.invoke_callee(callee, this_value, &arguments, Value::UNDEFINED);
2506 self.outcome_result(outcome)
2507 }
2508 HelperCall::Construct { callee, arguments } => {
2509 let arguments = {
2510 let read = self.machine.borrow().arguments_from_array(arguments);
2511 match read {
2512 Ok(arguments) => arguments,
2513 Err(failure) => return self.fail(failure),
2514 }
2515 };
2516 self.construct(callee, &arguments)
2517 }
2518 HelperCall::Import { specifier } => self.import_namespace(module, specifier),
2519 HelperCall::Truthy { value } => {
2520 HelperResult::normal(Value::boolean(self.machine.borrow().truthy(value)))
2521 }
2522 HelperCall::ResumeValue => {
2523 let resumed = self
2524 .activations
2525 .borrow_mut()
2526 .last_mut()
2527 .and_then(|activation| activation.pending_resume.take());
2528 match resumed {
2529 Some(value) => self.validated(value),
2530 None => self.fatal(RuntimeErrorKind::InvalidValue {
2531 value: Value::UNDEFINED,
2532 }),
2533 }
2534 }
2535 HelperCall::LoadGlobal { name } => {
2536 let resolved = self
2537 .machine
2538 .borrow()
2539 .load_global(module, ConstantId::new(name));
2540 match resolved {
2541 Ok(Some(value)) => self.validated(value),
2542 Ok(None) => {
2543 self.pending_throw.set(Some(PendingThrow {
2544 value: Value::UNDEFINED,
2545 origin: ThrowOrigin::ReferenceError {
2546 operation: "global is not defined",
2547 },
2548 }));
2549 HelperResult::throw(Value::UNDEFINED)
2550 }
2551 Err(kind) => self.fatal(kind),
2552 }
2553 }
2554 HelperCall::StoreGlobal { name, value } => {
2555 let stored =
2556 self.machine
2557 .borrow_mut()
2558 .store_global(module, ConstantId::new(name), value);
2559 match stored {
2560 Ok(()) => HelperResult::normal(Value::UNDEFINED),
2561 Err(failure) => self.fail(failure),
2562 }
2563 }
2564 HelperCall::TypeOfGlobal { name } => {
2565 let resolved = self
2566 .machine
2567 .borrow()
2568 .load_global(module, ConstantId::new(name));
2569 let text = match resolved {
2570 Ok(Some(value)) => EcmaString::from_utf8(self.machine.borrow().type_of(value)),
2571 Ok(None) => EcmaString::from_utf8("undefined"),
2572 Err(kind) => return self.fatal(kind),
2573 };
2574 self.allocated(HeapEntry::String(text))
2575 }
2576 HelperCall::LoadThis => HelperResult::normal(
2577 self.activations
2578 .borrow()
2579 .last()
2580 .map_or(Value::UNDEFINED, |activation| activation.this_value),
2581 ),
2582 HelperCall::LoadArguments => self.load_arguments(),
2583 HelperCall::LoadNewTarget => HelperResult::normal(
2584 self.activations
2585 .borrow()
2586 .last()
2587 .map_or(Value::UNDEFINED, |activation| activation.new_target),
2588 ),
2589 HelperCall::ArrayPush { array, value } => {
2590 let result = self.machine.borrow_mut().array_push(array, value);
2591 match result {
2592 Ok(()) => HelperResult::normal(Value::UNDEFINED),
2593 Err(failure) => self.fail(failure),
2594 }
2595 }
2596 HelperCall::ArrayExtend { array, iterable } => {
2597 let outcome = self.array_extend_active(array, iterable);
2598 self.outcome_result(outcome)
2599 }
2600 HelperCall::ObjectSpread { target, source } => {
2601 let result = self.machine.borrow_mut().object_spread(target, source);
2602 match result {
2603 Ok(()) => HelperResult::normal(Value::UNDEFINED),
2604 Err(failure) => self.fail(failure),
2605 }
2606 }
2607 HelperCall::SetPrototype { object, prototype } => {
2608 let result = self.machine.borrow_mut().set_prototype(object, prototype);
2609 match result {
2610 Ok(()) => HelperResult::normal(Value::UNDEFINED),
2611 Err(failure) => self.fail(failure),
2612 }
2613 }
2614 HelperCall::CreatePrivateName { description } => {
2615 let description = self.constant_text(module, description);
2616 self.allocated(HeapEntry::PrivateName { description })
2617 }
2618 HelperCall::CreateRegExp { pattern, flags } => {
2619 let pattern = self.constant_text(module, pattern);
2620 let flags = self.constant_text(module, flags);
2621 let prototype = self.machine.borrow().intrinsics.regexp_prototype();
2622 self.allocated(HeapEntry::RegExp {
2623 pattern,
2624 flags,
2625 properties: PropertyMap::default(),
2626 prototype: Some(prototype),
2627 extensible: true,
2628 })
2629 }
2630 HelperCall::GetIterator { src, kind } => match iterator_kind_from_selector(kind) {
2631 Some(kind) => self.outcome_result(self.get_iterator_active(src, kind)),
2632 None => self.fatal(RuntimeErrorKind::InvalidValue {
2633 value: Value::UNDEFINED,
2634 }),
2635 },
2636 HelperCall::IteratorNext {
2637 iterator,
2638 done_reg,
2639 value_reg,
2640 } => match self.iterator_next_active(iterator) {
2641 Ok((done, value)) => {
2642 let wrote_done = frame.try_set_register(done_reg, Value::boolean(done));
2643 let wrote_value = frame.try_set_register(value_reg, value);
2644 if wrote_done && wrote_value {
2645 HelperResult::normal(Value::UNDEFINED)
2646 } else {
2647 self.fatal(RuntimeErrorKind::InvalidValue {
2648 value: Value::UNDEFINED,
2649 })
2650 }
2651 }
2652 Err(outcome) => self.outcome_result(outcome),
2653 },
2654 HelperCall::Export { .. } => self.fail(EvalFailure::Throw(ThrowOrigin::TypeError {
2655 operation: "export outside an engine-owned module registry",
2656 })),
2657 HelperCall::ConsumeFuel { .. } => HelperResult::normal(Value::UNDEFINED),
2658 }
2659 }
2660}
2661
2662fn is_inline_instruction(instruction: Instruction) -> bool {
2663 match instruction {
2664 Instruction::Move { .. }
2665 | Instruction::Jump { .. }
2666 | Instruction::JumpIfTrue { .. }
2667 | Instruction::JumpIfFalse { .. }
2668 | Instruction::Return { .. }
2669 | Instruction::Halt
2670 | Instruction::Throw { .. }
2671 | Instruction::Suspend { .. } => true,
2672 Instruction::LoadConst { .. }
2673 | Instruction::Unary { .. }
2674 | Instruction::Binary { .. }
2675 | Instruction::CreateObject { .. }
2676 | Instruction::CreateArray { .. }
2677 | Instruction::CreateCell { .. }
2678 | Instruction::CreateClosure { .. }
2679 | Instruction::GetProperty { .. }
2680 | Instruction::SetProperty { .. }
2681 | Instruction::DeleteProperty { .. }
2682 | Instruction::DefineAccessor { .. }
2683 | Instruction::Call { .. }
2684 | Instruction::Construct { .. }
2685 | Instruction::LoadGlobal { .. }
2686 | Instruction::StoreGlobal { .. }
2687 | Instruction::TypeOfGlobal { .. }
2688 | Instruction::LoadThis { .. }
2689 | Instruction::LoadArguments { .. }
2690 | Instruction::LoadNewTarget { .. }
2691 | Instruction::ArrayPush { .. }
2692 | Instruction::ArrayExtend { .. }
2693 | Instruction::ObjectSpread { .. }
2694 | Instruction::SetPrototype { .. }
2695 | Instruction::CreatePrivateName { .. }
2696 | Instruction::CreateRegExp { .. }
2697 | Instruction::GetIterator { .. }
2698 | Instruction::IteratorNext { .. }
2699 | Instruction::Import { .. }
2700 | Instruction::Export { .. } => false,
2701 }
2702}
2703
2704pub fn run_linked_program<H: Host>(
2714 program: &Program<Verified>,
2715 entries: &dyn NativeEntryTable,
2716 host: &mut H,
2717 limits: &Limits,
2718) -> Result<ExecutionOutcome, NativeError> {
2719 let program_bytes = program.encode();
2720 if program_bytes != entries.program_bytes() {
2721 return Err(NativeError::ProgramMismatch);
2722 }
2723 let mut engine = NativeEngine::build(program, entries, host, limits.clone(), Backend::Linked);
2724 engine.run_linked()
2725}
2726
2727#[cfg(test)]
2728mod tests {
2729 use std::cell::{Cell, RefCell};
2730 use std::sync::Arc;
2731
2732 use bamts_bytecode::{
2733 BinaryOp, Binding, BindingId, BindingKind, Constant, ConstantId, Edge, EdgeId, EdgeKind,
2734 EdgeTarget, ExceptionHandler, Export, ExportSource, Function, FunctionFlags, FunctionId,
2735 Instruction, IteratorKind, Module, ModuleId, Pc, Program, ProgramModule, Register,
2736 Verified,
2737 };
2738 use bamts_native::{
2739 AbiError, Completion, CompletionTag, HelperCall, HelperResult, NativeEntryTable,
2740 NativeFrame, NativeOps, ShadowFrame, Value,
2741 };
2742
2743 use crate::{
2744 GeneratorState, HeapEntry, Host, Limits, Machine, PropertyMap, RuntimeError,
2745 RuntimeErrorKind, ThrowOrigin,
2746 };
2747
2748 use super::EcmaString;
2749 use super::{
2750 Activation, Backend, InvokeOutcome, NativeEngine, NativeError, PendingThrow,
2751 run_linked_program,
2752 };
2753
2754 fn reg(raw: u32) -> Register {
2755 Register::new(raw)
2756 }
2757
2758 fn pc(raw: u32) -> Pc {
2759 Pc::new(raw)
2760 }
2761
2762 fn cid(raw: u32) -> bamts_bytecode::ConstantId {
2763 bamts_bytecode::ConstantId::new(raw)
2764 }
2765
2766 fn entry_function(register_count: u32, code: Vec<Instruction>) -> Function {
2767 Function::new(
2768 None,
2769 0,
2770 0,
2771 register_count,
2772 FunctionFlags::default(),
2773 code,
2774 Vec::new(),
2775 )
2776 }
2777
2778 fn receiver_sum_function() -> Function {
2779 module_function(
2780 0,
2781 5,
2782 vec![
2783 Instruction::LoadThis { dst: reg(0) },
2784 Instruction::LoadConst {
2785 dst: reg(1),
2786 constant: cid(1),
2787 },
2788 Instruction::GetProperty {
2789 dst: reg(2),
2790 object: reg(0),
2791 key: reg(1),
2792 },
2793 Instruction::LoadArguments { dst: reg(3) },
2794 Instruction::LoadConst {
2795 dst: reg(1),
2796 constant: cid(2),
2797 },
2798 Instruction::GetProperty {
2799 dst: reg(4),
2800 object: reg(3),
2801 key: reg(1),
2802 },
2803 Instruction::Binary {
2804 dst: reg(2),
2805 op: BinaryOp::Add,
2806 left: reg(2),
2807 right: reg(4),
2808 },
2809 Instruction::LoadConst {
2810 dst: reg(1),
2811 constant: cid(3),
2812 },
2813 Instruction::GetProperty {
2814 dst: reg(4),
2815 object: reg(3),
2816 key: reg(1),
2817 },
2818 Instruction::Binary {
2819 dst: reg(2),
2820 op: BinaryOp::Add,
2821 left: reg(2),
2822 right: reg(4),
2823 },
2824 Instruction::Return { value: reg(2) },
2825 ],
2826 )
2827 }
2828
2829 fn verified(constants: Vec<Constant>, functions: Vec<Function>) -> Module<Verified> {
2830 Module::new(constants, functions, FunctionId::new(0))
2831 .verify()
2832 .expect("module verifies")
2833 }
2834
2835 fn module_function(captures: u32, registers: u32, code: Vec<Instruction>) -> Function {
2836 Function::new(
2837 None,
2838 captures,
2839 0,
2840 registers,
2841 FunctionFlags::default(),
2842 code,
2843 Vec::new(),
2844 )
2845 }
2846
2847 fn program_module(
2848 name: &str,
2849 mut constants: Vec<Constant>,
2850 functions: Vec<Function>,
2851 edges: Vec<Edge>,
2852 bindings: Vec<Binding>,
2853 exports: Vec<Export>,
2854 ) -> ProgramModule<Verified> {
2855 constants.insert(0, Constant::String(EcmaString::from_utf8(name)));
2856 ProgramModule {
2857 name: cid(0),
2858 code: Module::new(constants, functions, FunctionId::new(0))
2859 .verify()
2860 .expect("module fixture verifies"),
2861 edges,
2862 bindings,
2863 exports,
2864 }
2865 }
2866
2867 fn linked(modules: Vec<ProgramModule<Verified>>, entry: u32) -> Program<Verified> {
2868 Program::link(modules, ModuleId::new(entry)).expect("module fixture links")
2869 }
2870
2871 fn dynamic_cycle_program() -> Program<Verified> {
2872 let root = program_module(
2873 "root",
2874 vec![Constant::String(EcmaString::from_utf8("./target"))],
2875 vec![entry_function(
2876 3,
2877 vec![
2878 Instruction::Import {
2879 dst: reg(0),
2880 specifier: cid(1),
2881 },
2882 Instruction::Import {
2883 dst: reg(1),
2884 specifier: cid(1),
2885 },
2886 Instruction::Binary {
2887 dst: reg(2),
2888 op: BinaryOp::StrictEqual,
2889 left: reg(0),
2890 right: reg(1),
2891 },
2892 Instruction::Return { value: reg(2) },
2893 ],
2894 )],
2895 vec![Edge {
2896 specifier: cid(1),
2897 target: EdgeTarget::Local(ModuleId::new(1)),
2898 kind: EdgeKind::Dynamic,
2899 }],
2900 Vec::new(),
2901 Vec::new(),
2902 );
2903 let target = program_module(
2904 "target",
2905 vec![Constant::String(EcmaString::from_utf8("./root"))],
2906 vec![entry_function(1, vec![Instruction::Halt])],
2907 vec![Edge {
2908 specifier: cid(1),
2909 target: EdgeTarget::Local(ModuleId::new(0)),
2910 kind: EdgeKind::Static,
2911 }],
2912 Vec::new(),
2913 Vec::new(),
2914 );
2915 linked(vec![root, target], 0)
2916 }
2917
2918 fn assert_program_parity(
2919 program: &Program<Verified>,
2920 ) -> Result<crate::Execution, RuntimeError> {
2921 let limits = Limits::default();
2922 let mut interpreter_host = SilentHost;
2923 let interpreter = Machine::new(program, &mut interpreter_host, limits.clone()).run();
2924 let mut native_host = SilentHost;
2925 let entries = NoEntries;
2926 let native = NativeEngine::new(program, &entries, &mut native_host, limits).run();
2927 assert_eq!(interpreter, native);
2928 native
2929 }
2930
2931 struct NoEntries;
2933
2934 impl NativeEntryTable for NoEntries {
2935 fn program_bytes(&self) -> &[u8] {
2936 &[]
2937 }
2938
2939 fn invoke(
2940 &self,
2941 module_id: u32,
2942 function_id: u32,
2943 _frame: &mut ShadowFrame,
2944 _out: &mut Completion,
2945 ) -> Result<CompletionTag, AbiError> {
2946 Err(AbiError::UnknownFunction {
2947 module_id,
2948 function_id,
2949 })
2950 }
2951 }
2952
2953 #[test]
2954 fn microtask_checkpoint_matches_interpreter_and_native_engine() {
2955 let program = linked(
2956 vec![program_module(
2957 "root",
2958 vec![
2959 Constant::String(EcmaString::from_utf8("queueMicrotask")),
2960 Constant::String(EcmaString::from_utf8("observed")),
2961 Constant::Int32(7),
2962 Constant::Undefined,
2963 ],
2964 vec![
2965 entry_function(
2966 6,
2967 vec![
2968 Instruction::CreateArray { dst: reg(0) },
2969 Instruction::CreateClosure {
2970 dst: reg(1),
2971 function: FunctionId::new(1),
2972 captures: reg(0),
2973 },
2974 Instruction::LoadGlobal {
2975 dst: reg(2),
2976 name: cid(1),
2977 },
2978 Instruction::CreateArray { dst: reg(3) },
2979 Instruction::ArrayPush {
2980 array: reg(3),
2981 value: reg(1),
2982 },
2983 Instruction::LoadConst {
2984 dst: reg(4),
2985 constant: cid(4),
2986 },
2987 Instruction::Call {
2988 dst: reg(5),
2989 callee: reg(2),
2990 this_value: reg(4),
2991 arguments: reg(3),
2992 },
2993 Instruction::Return { value: reg(4) },
2994 ],
2995 ),
2996 module_function(
2997 0,
2998 1,
2999 vec![
3000 Instruction::LoadConst {
3001 dst: reg(0),
3002 constant: cid(3),
3003 },
3004 Instruction::StoreGlobal {
3005 name: cid(2),
3006 value: reg(0),
3007 },
3008 Instruction::Return { value: reg(0) },
3009 ],
3010 ),
3011 ],
3012 Vec::new(),
3013 Vec::new(),
3014 Vec::new(),
3015 )],
3016 0,
3017 );
3018 let observed = EcmaString::from_utf8("observed");
3019
3020 let mut interpreter_host = SilentHost;
3021 let mut interpreter = Machine::new(&program, &mut interpreter_host, Limits::default());
3022 interpreter.evaluate().unwrap();
3023 assert!(!interpreter.globals.contains_key(&observed));
3024 let interpreter_drain = interpreter.drain_microtasks().unwrap();
3025 let interpreter_value = interpreter.globals.get(&observed).copied();
3026
3027 let mut native_host = SilentHost;
3028 let engine = NativeEngine::new(&program, &NoEntries, &mut native_host, Limits::default());
3029 engine.machine.borrow_mut().instantiate_modules().unwrap();
3030 engine
3031 .evaluate_reference_module(program.entry())
3032 .unwrap()
3033 .expect("native entry completes");
3034 assert!(!engine.machine.borrow().globals.contains_key(&observed));
3035 let native_drain = engine.machine.borrow_mut().drain_microtasks().unwrap();
3036 let native_value = engine.machine.borrow().globals.get(&observed).copied();
3037
3038 assert_eq!(interpreter_drain, native_drain);
3039 assert_eq!(interpreter_value, Some(Value::int32(7)));
3040 assert_eq!(native_value, interpreter_value);
3041 }
3042
3043 #[derive(Default)]
3044 struct RecordingHost {
3045 stdout: Vec<u8>,
3046 }
3047
3048 impl Host for RecordingHost {
3049 fn write_stdout(&mut self, bytes: &[u8]) {
3050 self.stdout.extend_from_slice(bytes);
3051 }
3052 }
3053
3054 fn first_window(haystack: &[u8], needle: &[u8]) -> Option<usize> {
3055 haystack.windows(needle.len()).position(|w| w == needle)
3056 }
3057
3058 fn automatic_loop_program(callback: Function) -> Program<Verified> {
3059 linked(
3060 vec![program_module(
3061 "root",
3062 vec![
3063 Constant::String(EcmaString::from_utf8("console")),
3064 Constant::String(EcmaString::from_utf8("log")),
3065 Constant::String(EcmaString::from_utf8("sync")),
3066 Constant::String(EcmaString::from_utf8("async")),
3067 Constant::String(EcmaString::from_utf8("queueMicrotask")),
3068 Constant::Int32(42),
3069 Constant::Undefined,
3070 ],
3071 vec![
3072 entry_function(
3073 7,
3074 vec![
3075 Instruction::LoadGlobal { dst: reg(0), name: cid(1) },
3076 Instruction::LoadConst { dst: reg(2), constant: cid(3) },
3077 Instruction::LoadConst { dst: reg(1), constant: cid(2) },
3078 Instruction::GetProperty { dst: reg(1), object: reg(0), key: reg(1) },
3079 Instruction::CreateArray { dst: reg(3) },
3080 Instruction::ArrayPush { array: reg(3), value: reg(2) },
3081 Instruction::LoadConst { dst: reg(4), constant: cid(7) },
3082 Instruction::Call {
3083 dst: reg(5),
3084 callee: reg(1),
3085 this_value: reg(4),
3086 arguments: reg(3),
3087 },
3088 Instruction::CreateArray { dst: reg(3) },
3089 Instruction::CreateClosure {
3090 dst: reg(1),
3091 function: FunctionId::new(1),
3092 captures: reg(3),
3093 },
3094 Instruction::LoadGlobal { dst: reg(6), name: cid(5) },
3095 Instruction::CreateArray { dst: reg(3) },
3096 Instruction::ArrayPush { array: reg(3), value: reg(1) },
3097 Instruction::LoadConst { dst: reg(4), constant: cid(7) },
3098 Instruction::Call {
3099 dst: reg(5),
3100 callee: reg(6),
3101 this_value: reg(4),
3102 arguments: reg(3),
3103 },
3104 Instruction::LoadConst { dst: reg(0), constant: cid(6) },
3105 Instruction::Return { value: reg(0) },
3106 ],
3107 ),
3108 callback,
3109 ],
3110 Vec::new(),
3111 Vec::new(),
3112 Vec::new(),
3113 )],
3114 0,
3115 )
3116 }
3117
3118 fn queue_callback_microtask<H: Host>(
3119 engine: &mut NativeEngine<'_, '_, H>,
3120 function: FunctionId,
3121 ) {
3122 let mut machine = engine.machine.borrow_mut();
3123 let callback = machine
3124 .allocate(HeapEntry::Function {
3125 module: ModuleId::new(0),
3126 function,
3127 captures: Vec::new(),
3128 properties: PropertyMap::default(),
3129 prototype: Some(machine.intrinsics.function_prototype),
3130 extensible: true,
3131 })
3132 .expect("microtask callback allocates");
3133 let queue = machine
3134 .intrinsics
3135 .global("queueMicrotask")
3136 .expect("queueMicrotask is installed");
3137 machine
3138 .call_value(queue, Value::UNDEFINED, &[callback])
3139 .expect("microtask enqueues");
3140 }
3141
3142 #[test]
3143 fn automatic_loop_drains_microtasks_and_preserves_synchronous_result() {
3144 let program = automatic_loop_program(module_function(
3145 0,
3146 4,
3147 vec![
3148 Instruction::LoadGlobal { dst: reg(0), name: cid(1) },
3149 Instruction::LoadConst { dst: reg(2), constant: cid(4) },
3150 Instruction::LoadConst { dst: reg(1), constant: cid(2) },
3151 Instruction::GetProperty { dst: reg(1), object: reg(0), key: reg(1) },
3152 Instruction::CreateArray { dst: reg(3) },
3153 Instruction::ArrayPush { array: reg(3), value: reg(2) },
3154 Instruction::LoadConst { dst: reg(0), constant: cid(7) },
3155 Instruction::Call {
3156 dst: reg(2),
3157 callee: reg(1),
3158 this_value: reg(0),
3159 arguments: reg(3),
3160 },
3161 Instruction::Return { value: reg(0) },
3162 ],
3163 ));
3164
3165 let mut interpreter_host = RecordingHost::default();
3166 let interpreter = Machine::new(&program, &mut interpreter_host, Limits::default())
3167 .run()
3168 .expect("interpreter drains to quiescence");
3169 assert_eq!(interpreter.value, Value::int32(42));
3170 assert!(interpreter.outcome.stdout.is_empty());
3171 let interp_sync = first_window(&interpreter_host.stdout, b"sync");
3172 let interp_async = first_window(&interpreter_host.stdout, b"async");
3173 assert!(
3174 interp_sync.is_some_and(|s| interp_async.is_some_and(|a| s < a)),
3175 "host received sync-then-async bytes: {:?}",
3176 interpreter_host.stdout,
3177 );
3178
3179 let mut native_host = RecordingHost::default();
3180 let native = NativeEngine::new(&program, &NoEntries, &mut native_host, Limits::default())
3181 .run()
3182 .expect("reference native drains to quiescence");
3183 assert_eq!(native.value, Value::int32(42));
3184 assert!(native.outcome.stdout.is_empty());
3185 assert_eq!(native_host.stdout, interpreter_host.stdout);
3186
3187 let mut linked_host = RecordingHost::default();
3188 let linked_outcome = {
3189 let entries = ForeignEntries {
3190 program_bytes: program.encode(),
3191 invoked: Cell::new(false),
3192 };
3193 let mut linked = NativeEngine::build(
3194 &program,
3195 &entries,
3196 &mut linked_host,
3197 Limits::default(),
3198 Backend::Linked,
3199 );
3200 queue_callback_microtask(&mut linked, FunctionId::new(1));
3201 linked
3202 .run_linked()
3203 .expect("linked native drains to quiescence")
3204 };
3205 assert!(linked_outcome.stdout.is_empty());
3206 assert!(
3207 first_window(&linked_host.stdout, b"async").is_some(),
3208 "linked loop drained the pre-queued microtask: {:?}",
3209 linked_host.stdout,
3210 );
3211 }
3212
3213 #[test]
3214 fn automatic_loop_surfaces_uncaught_callback_error_across_entrypoints() {
3215 let program = automatic_loop_program(module_function(
3216 0,
3217 1,
3218 vec![
3219 Instruction::LoadConst { dst: reg(0), constant: cid(6) },
3220 Instruction::Throw { value: reg(0) },
3221 ],
3222 ));
3223
3224 let mut interpreter_host = SilentHost;
3225 let interpreter_err = Machine::new(&program, &mut interpreter_host, Limits::default())
3226 .run()
3227 .expect_err("interpreter surfaces the uncaught callback");
3228 let thrown = match &interpreter_err.kind {
3229 RuntimeErrorKind::UncaughtThrow { value, .. } => *value,
3230 other => panic!("expected UncaughtThrow, got {other:?}"),
3231 };
3232 assert_eq!(thrown, Value::int32(42));
3233
3234 let mut native_host = SilentHost;
3235 let native_err = NativeEngine::new(&program, &NoEntries, &mut native_host, Limits::default())
3236 .run()
3237 .expect_err("reference native surfaces the uncaught callback");
3238 assert_eq!(native_err.kind, interpreter_err.kind);
3239
3240 let mut linked_host = SilentHost;
3241 let linked_err = {
3242 let entries = ForeignEntries {
3243 program_bytes: program.encode(),
3244 invoked: Cell::new(false),
3245 };
3246 let mut linked = NativeEngine::build(
3247 &program,
3248 &entries,
3249 &mut linked_host,
3250 Limits::default(),
3251 Backend::Linked,
3252 );
3253 queue_callback_microtask(&mut linked, FunctionId::new(1));
3254 linked
3255 .run_linked()
3256 .expect_err("linked native surfaces the uncaught callback")
3257 };
3258 match linked_err {
3259 NativeError::Runtime(RuntimeError { ref kind, .. }) if *kind == interpreter_err.kind => {}
3260 other => panic!("linked mapped the callback throw: {other:?}"),
3261 }
3262 }
3263
3264 fn async_module_function(registers: u32, code: Vec<Instruction>) -> Function {
3265 Function::new(
3266 None,
3267 0,
3268 0,
3269 registers,
3270 FunctionFlags {
3271 is_async: true,
3272 is_generator: false,
3273 },
3274 code,
3275 Vec::new(),
3276 )
3277 }
3278
3279 #[test]
3284 fn async_call_matches_interpreter_and_native_engine() {
3285 let program = linked(
3286 vec![program_module(
3287 "root",
3288 vec![
3289 Constant::String(EcmaString::from_utf8("observed")),
3290 Constant::Int32(7),
3291 Constant::Undefined,
3292 ],
3293 vec![
3294 entry_function(
3295 5,
3296 vec![
3297 Instruction::CreateArray { dst: reg(0) },
3298 Instruction::CreateClosure {
3299 dst: reg(1),
3300 function: FunctionId::new(1),
3301 captures: reg(0),
3302 },
3303 Instruction::CreateArray { dst: reg(2) },
3304 Instruction::LoadConst {
3305 dst: reg(3),
3306 constant: cid(3),
3307 },
3308 Instruction::Call {
3309 dst: reg(4),
3310 callee: reg(1),
3311 this_value: reg(3),
3312 arguments: reg(2),
3313 },
3314 Instruction::Return { value: reg(3) },
3315 ],
3316 ),
3317 async_module_function(
3318 2,
3319 vec![
3320 Instruction::LoadConst {
3321 dst: reg(0),
3322 constant: cid(2),
3323 },
3324 Instruction::Suspend {
3325 dst: reg(1),
3326 src: reg(0),
3327 resume: pc(2),
3328 },
3329 Instruction::StoreGlobal {
3330 name: cid(1),
3331 value: reg(1),
3332 },
3333 Instruction::Return { value: reg(1) },
3334 ],
3335 ),
3336 ],
3337 Vec::new(),
3338 Vec::new(),
3339 Vec::new(),
3340 )],
3341 0,
3342 );
3343 let observed = EcmaString::from_utf8("observed");
3344
3345 let mut interpreter_host = SilentHost;
3346 let mut interpreter = Machine::new(&program, &mut interpreter_host, Limits::default());
3347 interpreter.evaluate().unwrap();
3348 assert!(!interpreter.globals.contains_key(&observed));
3349 let interpreter_drain = interpreter.drain_microtasks().unwrap();
3350 let interpreter_value = interpreter.globals.get(&observed).copied();
3351
3352 let mut native_host = SilentHost;
3353 let engine = NativeEngine::new(&program, &NoEntries, &mut native_host, Limits::default());
3354 engine.machine.borrow_mut().instantiate_modules().unwrap();
3355 engine
3356 .evaluate_reference_module(program.entry())
3357 .unwrap()
3358 .expect("native entry completes");
3359 assert!(!engine.machine.borrow().globals.contains_key(&observed));
3360 let native_drain = engine.machine.borrow_mut().drain_microtasks().unwrap();
3361 let native_value = engine.machine.borrow().globals.get(&observed).copied();
3362
3363 let mut linked_host = SilentHost;
3364 let linked = NativeEngine::build(
3365 &program,
3366 &NoEntries,
3367 &mut linked_host,
3368 Limits::default(),
3369 Backend::Linked,
3370 );
3371 linked.machine.borrow_mut().instantiate_modules().unwrap();
3372 assert!(matches!(
3373 linked.invoke_runtime(
3374 crate::RuntimeFunction {
3375 module: ModuleId::new(0),
3376 function: FunctionId::new(1),
3377 },
3378 &[],
3379 Value::UNDEFINED,
3380 Value::UNDEFINED,
3381 &[],
3382 ),
3383 InvokeOutcome::Value(_)
3384 ));
3385 assert!(!linked.machine.borrow().globals.contains_key(&observed));
3386 let linked_drain = linked.machine.borrow_mut().drain_microtasks().unwrap();
3387 let linked_value = linked.machine.borrow().globals.get(&observed).copied();
3388
3389 assert_eq!(interpreter_drain, native_drain);
3390 assert_eq!(interpreter_value, Some(Value::int32(7)));
3391 assert_eq!(native_value, interpreter_value);
3392 assert_eq!(linked_drain, interpreter_drain);
3393 assert_eq!(linked_value, interpreter_value);
3394 }
3395
3396 #[test]
3399 fn native_construct_of_async_function_is_a_type_error() {
3400 let program = linked(
3401 vec![program_module(
3402 "root",
3403 Vec::new(),
3404 vec![
3405 entry_function(
3406 4,
3407 vec![
3408 Instruction::CreateArray { dst: reg(0) },
3409 Instruction::CreateClosure {
3410 dst: reg(1),
3411 function: FunctionId::new(1),
3412 captures: reg(0),
3413 },
3414 Instruction::CreateArray { dst: reg(2) },
3415 Instruction::Construct {
3416 dst: reg(3),
3417 callee: reg(1),
3418 arguments: reg(2),
3419 },
3420 Instruction::Return { value: reg(3) },
3421 ],
3422 ),
3423 async_module_function(1, vec![Instruction::Halt]),
3424 ],
3425 Vec::new(),
3426 Vec::new(),
3427 Vec::new(),
3428 )],
3429 0,
3430 );
3431 let mut host = SilentHost;
3432 let result = NativeEngine::new(&program, &NoEntries, &mut host, Limits::default()).run();
3433 assert!(matches!(
3434 result,
3435 Err(RuntimeError {
3436 kind: RuntimeErrorKind::UncaughtThrow {
3437 origin: ThrowOrigin::TypeError { .. },
3438 ..
3439 },
3440 ..
3441 })
3442 ));
3443 }
3444
3445 #[derive(Default)]
3446 struct RecordingEntries {
3447 program_bytes: Vec<u8>,
3448 invoked: RefCell<Vec<(u32, u32)>>,
3449 }
3450
3451 impl NativeEntryTable for RecordingEntries {
3452 fn program_bytes(&self) -> &[u8] {
3453 &self.program_bytes
3454 }
3455
3456 fn invoke(
3457 &self,
3458 module_id: u32,
3459 function_id: u32,
3460 _frame: &mut ShadowFrame,
3461 out: &mut Completion,
3462 ) -> Result<CompletionTag, AbiError> {
3463 self.invoked.borrow_mut().push((module_id, function_id));
3464 *out = Completion::new(Value::UNDEFINED);
3465 Ok(CompletionTag::Normal)
3466 }
3467 }
3468
3469 struct ForeignEntries {
3470 program_bytes: Vec<u8>,
3471 invoked: Cell<bool>,
3472 }
3473
3474 impl NativeEntryTable for ForeignEntries {
3475 fn program_bytes(&self) -> &[u8] {
3476 &self.program_bytes
3477 }
3478
3479 fn invoke(
3480 &self,
3481 _module_id: u32,
3482 _function_id: u32,
3483 _frame: &mut ShadowFrame,
3484 out: &mut Completion,
3485 ) -> Result<CompletionTag, AbiError> {
3486 self.invoked.set(true);
3487 *out = Completion::new(Value::UNDEFINED);
3488 Ok(CompletionTag::Normal)
3489 }
3490 }
3491
3492 struct SmokeEntries {
3493 program_bytes: Vec<u8>,
3494 invoked: Cell<Option<u32>>,
3495 }
3496
3497 impl NativeEntryTable for SmokeEntries {
3498 fn program_bytes(&self) -> &[u8] {
3499 &self.program_bytes
3500 }
3501
3502 fn invoke(
3503 &self,
3504 module_id: u32,
3505 function_id: u32,
3506 _frame: &mut ShadowFrame,
3507 out: &mut Completion,
3508 ) -> Result<CompletionTag, AbiError> {
3509 assert_eq!(module_id, 0);
3510 self.invoked.set(Some(function_id));
3511 *out = Completion::new(Value::UNDEFINED);
3512 Ok(CompletionTag::Normal)
3513 }
3514 }
3515
3516 #[derive(Default)]
3517 struct FailingEntries {
3518 program_bytes: Vec<u8>,
3519 }
3520
3521 impl NativeEntryTable for FailingEntries {
3522 fn program_bytes(&self) -> &[u8] {
3523 &self.program_bytes
3524 }
3525
3526 fn invoke(
3527 &self,
3528 module_id: u32,
3529 function_id: u32,
3530 _frame: &mut ShadowFrame,
3531 _out: &mut Completion,
3532 ) -> Result<CompletionTag, AbiError> {
3533 Err(AbiError::UnknownFunction {
3534 module_id,
3535 function_id,
3536 })
3537 }
3538 }
3539
3540 struct ThrowEntries;
3541
3542 impl NativeEntryTable for ThrowEntries {
3543 fn program_bytes(&self) -> &[u8] {
3544 &[]
3545 }
3546
3547 fn invoke(
3548 &self,
3549 _module_id: u32,
3550 _function_id: u32,
3551 _frame: &mut ShadowFrame,
3552 out: &mut Completion,
3553 ) -> Result<CompletionTag, AbiError> {
3554 *out = Completion::new(Value::UNDEFINED);
3555 Ok(CompletionTag::Throw)
3556 }
3557 }
3558
3559 struct FatalEntries;
3560
3561 impl NativeEntryTable for FatalEntries {
3562 fn program_bytes(&self) -> &[u8] {
3563 &[]
3564 }
3565
3566 fn invoke(
3567 &self,
3568 _module_id: u32,
3569 _function_id: u32,
3570 _frame: &mut ShadowFrame,
3571 out: &mut Completion,
3572 ) -> Result<CompletionTag, AbiError> {
3573 *out = Completion::new(Value::UNDEFINED);
3574 Ok(CompletionTag::FatalTrap)
3575 }
3576 }
3577
3578 #[test]
3579 fn native_program_keeps_same_name_globals_module_local() {
3580 let dependency = |name: &str, value: i32| {
3581 program_module(
3582 name,
3583 vec![
3584 Constant::String(EcmaString::from_utf8("x")),
3585 Constant::Int32(value),
3586 ],
3587 vec![module_function(
3588 0,
3589 1,
3590 vec![
3591 Instruction::LoadConst {
3592 dst: reg(0),
3593 constant: cid(2),
3594 },
3595 Instruction::StoreGlobal {
3596 name: cid(1),
3597 value: reg(0),
3598 },
3599 Instruction::Return { value: reg(0) },
3600 ],
3601 )],
3602 Vec::new(),
3603 vec![Binding {
3604 name: cid(1),
3605 kind: BindingKind::Hoisted,
3606 }],
3607 vec![Export {
3608 name: cid(1),
3609 source: ExportSource::Local(BindingId::new(0)),
3610 }],
3611 )
3612 };
3613 let root = program_module(
3614 "root",
3615 vec![
3616 Constant::String(EcmaString::from_utf8("left")),
3617 Constant::String(EcmaString::from_utf8("right")),
3618 Constant::String(EcmaString::from_utf8("x")),
3619 Constant::String(EcmaString::from_utf8("one")),
3620 Constant::String(EcmaString::from_utf8("two")),
3621 ],
3622 vec![module_function(
3623 0,
3624 3,
3625 vec![
3626 Instruction::LoadGlobal {
3627 dst: reg(0),
3628 name: cid(1),
3629 },
3630 Instruction::LoadGlobal {
3631 dst: reg(1),
3632 name: cid(2),
3633 },
3634 Instruction::Binary {
3635 dst: reg(2),
3636 op: BinaryOp::Add,
3637 left: reg(0),
3638 right: reg(1),
3639 },
3640 Instruction::Return { value: reg(2) },
3641 ],
3642 )],
3643 vec![
3644 Edge {
3645 specifier: cid(3),
3646 target: EdgeTarget::Local(ModuleId::new(0)),
3647 kind: EdgeKind::Static,
3648 },
3649 Edge {
3650 specifier: cid(4),
3651 target: EdgeTarget::Local(ModuleId::new(1)),
3652 kind: EdgeKind::Static,
3653 },
3654 ],
3655 vec![
3656 Binding {
3657 name: cid(1),
3658 kind: BindingKind::Imported {
3659 edge: EdgeId::new(0),
3660 name: cid(3),
3661 },
3662 },
3663 Binding {
3664 name: cid(2),
3665 kind: BindingKind::Imported {
3666 edge: EdgeId::new(1),
3667 name: cid(3),
3668 },
3669 },
3670 ],
3671 Vec::new(),
3672 );
3673 let execution = assert_program_parity(&linked(
3674 vec![dependency("one", 1), dependency("two", 2), root],
3675 2,
3676 ))
3677 .unwrap();
3678 assert_eq!(execution.value, Value::int32(3));
3679 }
3680
3681 #[test]
3682 fn native_program_preserves_live_mutation_and_nested_closure_module() {
3683 let dependency = program_module(
3684 "dependency",
3685 vec![
3686 Constant::String(EcmaString::from_utf8("x")),
3687 Constant::Int32(1),
3688 Constant::Int32(2),
3689 Constant::String(EcmaString::from_utf8("set")),
3690 ],
3691 vec![
3692 module_function(
3693 0,
3694 3,
3695 vec![
3696 Instruction::LoadConst {
3697 dst: reg(0),
3698 constant: cid(2),
3699 },
3700 Instruction::StoreGlobal {
3701 name: cid(1),
3702 value: reg(0),
3703 },
3704 Instruction::CreateArray { dst: reg(1) },
3705 Instruction::CreateClosure {
3706 dst: reg(2),
3707 function: FunctionId::new(1),
3708 captures: reg(1),
3709 },
3710 Instruction::StoreGlobal {
3711 name: cid(4),
3712 value: reg(2),
3713 },
3714 Instruction::Return { value: reg(0) },
3715 ],
3716 ),
3717 module_function(
3718 0,
3719 1,
3720 vec![
3721 Instruction::LoadConst {
3722 dst: reg(0),
3723 constant: cid(3),
3724 },
3725 Instruction::StoreGlobal {
3726 name: cid(1),
3727 value: reg(0),
3728 },
3729 Instruction::Return { value: reg(0) },
3730 ],
3731 ),
3732 ],
3733 Vec::new(),
3734 vec![
3735 Binding {
3736 name: cid(1),
3737 kind: BindingKind::Hoisted,
3738 },
3739 Binding {
3740 name: cid(4),
3741 kind: BindingKind::Hoisted,
3742 },
3743 ],
3744 vec![
3745 Export {
3746 name: cid(1),
3747 source: ExportSource::Local(BindingId::new(0)),
3748 },
3749 Export {
3750 name: cid(4),
3751 source: ExportSource::Local(BindingId::new(1)),
3752 },
3753 ],
3754 );
3755 let root = program_module(
3756 "root",
3757 vec![
3758 Constant::String(EcmaString::from_utf8("set")),
3759 Constant::String(EcmaString::from_utf8("x")),
3760 Constant::String(EcmaString::from_utf8("dependency")),
3761 ],
3762 vec![module_function(
3763 0,
3764 3,
3765 vec![
3766 Instruction::LoadGlobal {
3767 dst: reg(0),
3768 name: cid(1),
3769 },
3770 Instruction::CreateArray { dst: reg(1) },
3771 Instruction::Call {
3772 dst: reg(2),
3773 callee: reg(0),
3774 this_value: reg(1),
3775 arguments: reg(1),
3776 },
3777 Instruction::LoadGlobal {
3778 dst: reg(0),
3779 name: cid(2),
3780 },
3781 Instruction::Return { value: reg(0) },
3782 ],
3783 )],
3784 vec![Edge {
3785 specifier: cid(3),
3786 target: EdgeTarget::Local(ModuleId::new(0)),
3787 kind: EdgeKind::Static,
3788 }],
3789 vec![
3790 Binding {
3791 name: cid(1),
3792 kind: BindingKind::Imported {
3793 edge: EdgeId::new(0),
3794 name: cid(1),
3795 },
3796 },
3797 Binding {
3798 name: cid(2),
3799 kind: BindingKind::Imported {
3800 edge: EdgeId::new(0),
3801 name: cid(2),
3802 },
3803 },
3804 ],
3805 Vec::new(),
3806 );
3807 assert_eq!(
3808 assert_program_parity(&linked(vec![dependency, root], 1))
3809 .unwrap()
3810 .value,
3811 Value::int32(2)
3812 );
3813 }
3814
3815 #[test]
3816 fn native_program_cycle_observes_temporal_dead_zone() {
3817 let first = program_module(
3818 "first",
3819 vec![
3820 Constant::String(EcmaString::from_utf8("a")),
3821 Constant::Int32(1),
3822 Constant::String(EcmaString::from_utf8("second")),
3823 ],
3824 vec![module_function(
3825 0,
3826 1,
3827 vec![
3828 Instruction::LoadConst {
3829 dst: reg(0),
3830 constant: cid(2),
3831 },
3832 Instruction::StoreGlobal {
3833 name: cid(1),
3834 value: reg(0),
3835 },
3836 Instruction::Return { value: reg(0) },
3837 ],
3838 )],
3839 vec![Edge {
3840 specifier: cid(3),
3841 target: EdgeTarget::Local(ModuleId::new(1)),
3842 kind: EdgeKind::Static,
3843 }],
3844 vec![Binding {
3845 name: cid(1),
3846 kind: BindingKind::Lexical,
3847 }],
3848 vec![Export {
3849 name: cid(1),
3850 source: ExportSource::Local(BindingId::new(0)),
3851 }],
3852 );
3853 let second = program_module(
3854 "second",
3855 vec![
3856 Constant::String(EcmaString::from_utf8("a")),
3857 Constant::String(EcmaString::from_utf8("first")),
3858 ],
3859 vec![module_function(
3860 0,
3861 1,
3862 vec![
3863 Instruction::LoadGlobal {
3864 dst: reg(0),
3865 name: cid(1),
3866 },
3867 Instruction::Return { value: reg(0) },
3868 ],
3869 )],
3870 vec![Edge {
3871 specifier: cid(2),
3872 target: EdgeTarget::Local(ModuleId::new(0)),
3873 kind: EdgeKind::Static,
3874 }],
3875 vec![Binding {
3876 name: cid(1),
3877 kind: BindingKind::Imported {
3878 edge: EdgeId::new(0),
3879 name: cid(1),
3880 },
3881 }],
3882 Vec::new(),
3883 );
3884 let error = assert_program_parity(&linked(vec![first, second], 0)).unwrap_err();
3885 assert!(matches!(
3886 error.kind,
3887 RuntimeErrorKind::TemporalDeadZone { module, binding }
3888 if module == ModuleId::new(1) && binding == BindingId::new(0)
3889 ));
3890 }
3891
3892 #[test]
3893 fn native_program_namespace_reads_shared_export_cell() {
3894 let dependency = program_module(
3895 "dependency",
3896 vec![
3897 Constant::String(EcmaString::from_utf8("x")),
3898 Constant::Int32(7),
3899 ],
3900 vec![module_function(
3901 0,
3902 1,
3903 vec![
3904 Instruction::LoadConst {
3905 dst: reg(0),
3906 constant: cid(2),
3907 },
3908 Instruction::StoreGlobal {
3909 name: cid(1),
3910 value: reg(0),
3911 },
3912 Instruction::Return { value: reg(0) },
3913 ],
3914 )],
3915 Vec::new(),
3916 vec![Binding {
3917 name: cid(1),
3918 kind: BindingKind::Hoisted,
3919 }],
3920 vec![Export {
3921 name: cid(1),
3922 source: ExportSource::Local(BindingId::new(0)),
3923 }],
3924 );
3925 let root = program_module(
3926 "root",
3927 vec![
3928 Constant::String(EcmaString::from_utf8("ns")),
3929 Constant::String(EcmaString::from_utf8("x")),
3930 Constant::String(EcmaString::from_utf8("dependency")),
3931 ],
3932 vec![module_function(
3933 0,
3934 3,
3935 vec![
3936 Instruction::LoadGlobal {
3937 dst: reg(0),
3938 name: cid(1),
3939 },
3940 Instruction::LoadConst {
3941 dst: reg(1),
3942 constant: cid(2),
3943 },
3944 Instruction::GetProperty {
3945 dst: reg(2),
3946 object: reg(0),
3947 key: reg(1),
3948 },
3949 Instruction::Return { value: reg(2) },
3950 ],
3951 )],
3952 vec![Edge {
3953 specifier: cid(3),
3954 target: EdgeTarget::Local(ModuleId::new(0)),
3955 kind: EdgeKind::Static,
3956 }],
3957 vec![Binding {
3958 name: cid(1),
3959 kind: BindingKind::Namespace {
3960 edge: EdgeId::new(0),
3961 },
3962 }],
3963 Vec::new(),
3964 );
3965 assert_eq!(
3966 assert_program_parity(&linked(vec![dependency, root], 1))
3967 .unwrap()
3968 .value,
3969 Value::int32(7)
3970 );
3971 }
3972
3973 #[test]
3974 fn native_program_evaluates_duplicate_static_dependency_once() {
3975 let dependency = program_module(
3976 "dependency",
3977 vec![
3978 Constant::String(EcmaString::from_utf8("count")),
3979 Constant::Int32(0),
3980 Constant::Int32(1),
3981 ],
3982 vec![module_function(
3983 0,
3984 2,
3985 vec![
3986 Instruction::LoadGlobal {
3987 dst: reg(0),
3988 name: cid(1),
3989 },
3990 Instruction::JumpIfFalse {
3991 condition: reg(0),
3992 target: pc(4),
3993 },
3994 Instruction::LoadConst {
3995 dst: reg(1),
3996 constant: cid(3),
3997 },
3998 Instruction::Jump { target: pc(6) },
3999 Instruction::LoadConst {
4000 dst: reg(0),
4001 constant: cid(2),
4002 },
4003 Instruction::LoadConst {
4004 dst: reg(1),
4005 constant: cid(3),
4006 },
4007 Instruction::Binary {
4008 dst: reg(0),
4009 op: BinaryOp::Add,
4010 left: reg(0),
4011 right: reg(1),
4012 },
4013 Instruction::StoreGlobal {
4014 name: cid(1),
4015 value: reg(0),
4016 },
4017 Instruction::Return { value: reg(0) },
4018 ],
4019 )],
4020 Vec::new(),
4021 vec![Binding {
4022 name: cid(1),
4023 kind: BindingKind::Hoisted,
4024 }],
4025 vec![Export {
4026 name: cid(1),
4027 source: ExportSource::Local(BindingId::new(0)),
4028 }],
4029 );
4030 let root = program_module(
4031 "root",
4032 vec![
4033 Constant::String(EcmaString::from_utf8("count")),
4034 Constant::String(EcmaString::from_utf8("dependency")),
4035 Constant::String(EcmaString::from_utf8("dependency-again")),
4036 ],
4037 vec![module_function(
4038 0,
4039 1,
4040 vec![
4041 Instruction::LoadGlobal {
4042 dst: reg(0),
4043 name: cid(1),
4044 },
4045 Instruction::Return { value: reg(0) },
4046 ],
4047 )],
4048 vec![
4049 Edge {
4050 specifier: cid(2),
4051 target: EdgeTarget::Local(ModuleId::new(0)),
4052 kind: EdgeKind::Static,
4053 },
4054 Edge {
4055 specifier: cid(3),
4056 target: EdgeTarget::Local(ModuleId::new(0)),
4057 kind: EdgeKind::Static,
4058 },
4059 ],
4060 vec![Binding {
4061 name: cid(1),
4062 kind: BindingKind::Imported {
4063 edge: EdgeId::new(0),
4064 name: cid(1),
4065 },
4066 }],
4067 Vec::new(),
4068 );
4069 assert_eq!(
4070 assert_program_parity(&linked(vec![dependency, root], 1))
4071 .unwrap()
4072 .value,
4073 Value::int32(1)
4074 );
4075 }
4076
4077 #[test]
4078 fn native_program_memoizes_thrown_object_identity() {
4079 let module = program_module(
4080 "throws",
4081 Vec::new(),
4082 vec![module_function(
4083 0,
4084 1,
4085 vec![
4086 Instruction::CreateObject { dst: reg(0) },
4087 Instruction::Throw { value: reg(0) },
4088 ],
4089 )],
4090 Vec::new(),
4091 Vec::new(),
4092 Vec::new(),
4093 );
4094 let error = assert_program_parity(&linked(vec![module], 0)).unwrap_err();
4095 assert!(matches!(error.kind, RuntimeErrorKind::UncaughtThrow { .. }));
4096 }
4097
4098 struct SilentHost;
4099 impl Host for SilentHost {}
4100
4101 #[test]
4102 fn native_matches_interpreter_on_arithmetic() {
4103 let module = verified(
4104 vec![Constant::Int32(3), Constant::Int32(4)],
4105 vec![entry_function(
4106 2,
4107 vec![
4108 Instruction::LoadConst {
4109 dst: reg(0),
4110 constant: cid(0),
4111 },
4112 Instruction::LoadConst {
4113 dst: reg(1),
4114 constant: cid(1),
4115 },
4116 Instruction::Binary {
4117 dst: reg(0),
4118 op: BinaryOp::Add,
4119 left: reg(0),
4120 right: reg(1),
4121 },
4122 Instruction::Return { value: reg(0) },
4123 ],
4124 )],
4125 );
4126 let value = assert_parity(&module, || SilentHost);
4127 assert_eq!(value.as_int32(), Some(7));
4128 }
4129
4130 #[test]
4131 fn reference_and_interpreter_charge_each_mixed_instruction_once() {
4132 let module = verified(
4133 vec![Constant::Int32(1)],
4134 vec![entry_function(
4135 3,
4136 vec![
4137 Instruction::LoadConst {
4138 dst: reg(0),
4139 constant: cid(0),
4140 },
4141 Instruction::Move {
4142 dst: reg(1),
4143 src: reg(0),
4144 },
4145 Instruction::Binary {
4146 dst: reg(2),
4147 op: BinaryOp::Add,
4148 left: reg(0),
4149 right: reg(1),
4150 },
4151 Instruction::Jump { target: pc(4) },
4152 Instruction::Halt,
4153 ],
4154 )],
4155 );
4156 let program = one_module_program(&module);
4157
4158 for fuel in [0, 4, 5] {
4159 let limits = Limits {
4160 fuel,
4161 ..Limits::default()
4162 };
4163 let mut interpreter_host = SilentHost;
4164 let interpreter = Machine::new(&program, &mut interpreter_host, limits.clone()).run();
4165 let mut reference_host = SilentHost;
4166 let reference =
4167 NativeEngine::new(&program, &NoEntries, &mut reference_host, limits).run();
4168 assert_eq!(interpreter, reference, "fuel={fuel}");
4169 if fuel == 5 {
4170 assert!(reference.is_ok(), "N instructions must fit fuel N");
4171 } else {
4172 assert!(
4173 matches!(
4174 reference,
4175 Err(RuntimeError {
4176 kind: RuntimeErrorKind::FuelExhausted { limit },
4177 ..
4178 }) if limit == fuel
4179 ),
4180 "fuel={fuel} must exhaust before the next instruction"
4181 );
4182 }
4183 }
4184 }
4185
4186 #[test]
4187 fn native_reservations_share_interpreter_depth_and_register_ceilings() {
4188 let module = verified(Vec::new(), vec![entry_function(2, vec![Instruction::Halt])]);
4189 let program = one_module_program(&module);
4190 let mut host = SilentHost;
4191 let mut machine = Machine::new(
4192 &program,
4193 &mut host,
4194 Limits {
4195 max_call_depth: 2,
4196 max_total_registers: 3,
4197 ..Limits::default()
4198 },
4199 );
4200
4201 assert_eq!(machine.frames.len(), 1);
4202 assert_eq!(machine.live_registers, 2);
4203 machine.reserve_native_activation(1).unwrap();
4204 assert_eq!((machine.native_depth, machine.live_registers), (1, 3));
4205 assert!(matches!(
4206 machine.reserve_native_activation(0),
4207 Err(RuntimeErrorKind::CallDepthExceeded { limit: 2 })
4208 ));
4209 assert_eq!(
4210 (machine.native_depth, machine.live_registers),
4211 (1, 3),
4212 "failed depth reservation is atomic"
4213 );
4214 machine.release_native_activation(1);
4215
4216 machine.limits.max_call_depth = 3;
4217 machine.limits.max_total_registers = 2;
4218 assert!(matches!(
4219 machine.reserve_native_activation(1),
4220 Err(RuntimeErrorKind::RegisterLimitExceeded { limit: 2 })
4221 ));
4222 assert_eq!(
4223 (machine.native_depth, machine.live_registers),
4224 (0, 2),
4225 "failed register reservation is atomic"
4226 );
4227 }
4228
4229 #[test]
4230 fn native_matches_interpreter_on_loop_and_branches() {
4231 let module = verified(
4234 vec![Constant::Int32(0), Constant::Int32(1), Constant::Int32(4)],
4235 vec![entry_function(
4236 5,
4237 vec![
4238 Instruction::LoadConst {
4239 dst: reg(0),
4240 constant: cid(0),
4241 },
4242 Instruction::LoadConst {
4243 dst: reg(1),
4244 constant: cid(1),
4245 },
4246 Instruction::LoadConst {
4247 dst: reg(2),
4248 constant: cid(2),
4249 },
4250 Instruction::LoadConst {
4251 dst: reg(3),
4252 constant: cid(1),
4253 },
4254 Instruction::Binary {
4255 dst: reg(4),
4256 op: BinaryOp::LessThan,
4257 left: reg(1),
4258 right: reg(2),
4259 },
4260 Instruction::JumpIfFalse {
4261 condition: reg(4),
4262 target: pc(9),
4263 },
4264 Instruction::Binary {
4265 dst: reg(0),
4266 op: BinaryOp::Add,
4267 left: reg(0),
4268 right: reg(1),
4269 },
4270 Instruction::Binary {
4271 dst: reg(1),
4272 op: BinaryOp::Add,
4273 left: reg(1),
4274 right: reg(3),
4275 },
4276 Instruction::Jump { target: pc(4) },
4277 Instruction::Return { value: reg(0) },
4278 ],
4279 )],
4280 );
4281 let value = assert_parity(&module, || SilentHost);
4282 assert_eq!(value.as_int32(), Some(6));
4283 }
4284
4285 #[test]
4286 fn native_matches_interpreter_on_object_property_roundtrip() {
4287 let module = verified(
4288 vec![
4289 Constant::String(EcmaString::from_utf8("x")),
4290 Constant::Int32(5),
4291 ],
4292 vec![entry_function(
4293 3,
4294 vec![
4295 Instruction::CreateObject { dst: reg(0) },
4296 Instruction::LoadConst {
4297 dst: reg(1),
4298 constant: cid(0),
4299 },
4300 Instruction::LoadConst {
4301 dst: reg(2),
4302 constant: cid(1),
4303 },
4304 Instruction::SetProperty {
4305 object: reg(0),
4306 key: reg(1),
4307 value: reg(2),
4308 },
4309 Instruction::GetProperty {
4310 dst: reg(2),
4311 object: reg(0),
4312 key: reg(1),
4313 },
4314 Instruction::Return { value: reg(2) },
4315 ],
4316 )],
4317 );
4318 let value = assert_parity(&module, || SilentHost);
4319 assert_eq!(value.as_int32(), Some(5));
4320 }
4321
4322 #[test]
4323 fn native_matches_interpreter_on_closure_call() {
4324 let entry = entry_function(
4326 3,
4327 vec![
4328 Instruction::CreateArray { dst: reg(0) },
4329 Instruction::CreateClosure {
4330 dst: reg(1),
4331 function: FunctionId::new(1),
4332 captures: reg(0),
4333 },
4334 Instruction::CreateArray { dst: reg(0) },
4335 Instruction::LoadConst {
4336 dst: reg(2),
4337 constant: cid(0),
4338 },
4339 Instruction::Call {
4340 dst: reg(0),
4341 callee: reg(1),
4342 this_value: reg(2),
4343 arguments: reg(0),
4344 },
4345 Instruction::Return { value: reg(0) },
4346 ],
4347 );
4348 let callee = Function::new(
4349 None,
4350 0,
4351 0,
4352 1,
4353 FunctionFlags::default(),
4354 vec![
4355 Instruction::LoadConst {
4356 dst: reg(0),
4357 constant: cid(1),
4358 },
4359 Instruction::Return { value: reg(0) },
4360 ],
4361 Vec::new(),
4362 );
4363 let module = verified(
4364 vec![Constant::Undefined, Constant::Int32(42)],
4365 vec![entry, callee],
4366 );
4367 let value = assert_parity(&module, || SilentHost);
4368 assert_eq!(value.as_int32(), Some(42));
4369 }
4370
4371 #[test]
4372 fn native_matches_interpreter_on_throw_and_catch() {
4373 let module = verified(
4374 vec![Constant::Int32(99)],
4375 vec![Function::new(
4376 None,
4377 0,
4378 0,
4379 2,
4380 FunctionFlags::default(),
4381 vec![
4382 Instruction::LoadConst {
4383 dst: reg(0),
4384 constant: cid(0),
4385 },
4386 Instruction::Throw { value: reg(0) },
4387 Instruction::Return { value: reg(1) },
4388 ],
4389 vec![ExceptionHandler {
4390 start: pc(0),
4391 end: pc(2),
4392 handler: pc(2),
4393 catch_register: reg(1),
4394 }],
4395 )],
4396 );
4397 let value = assert_parity(&module, || SilentHost);
4398 assert_eq!(value.as_int32(), Some(99));
4399 }
4400
4401 fn one_module_program(module: &Module<Verified>) -> Program<Verified> {
4402 let mut constants = module.constants().to_vec();
4403 let name = ConstantId::new(constants.len() as u32);
4404 constants.push(Constant::String(EcmaString::from_utf8("test-module")));
4405 let code = Module::new(constants, module.functions().to_vec(), module.entry())
4406 .verify()
4407 .expect("test module remains verified");
4408 Program::link(
4409 vec![ProgramModule {
4410 name,
4411 code,
4412 edges: Vec::new(),
4413 bindings: Vec::new(),
4414 exports: Vec::new(),
4415 }],
4416 ModuleId::new(0),
4417 )
4418 .expect("one-module reference program links")
4419 }
4420
4421 fn assert_parity<H: Host, F: Fn() -> H>(module: &Module<Verified>, make_host: F) -> Value {
4422 let limits = Limits::default();
4423 let program = one_module_program(module);
4424
4425 let mut interp_host = make_host();
4426 let interpreter = Machine::new(&program, &mut interp_host, limits.clone())
4427 .run()
4428 .expect("interpreter runs");
4429
4430 let mut native_host = make_host();
4431 let entries = NoEntries;
4432 let native = NativeEngine::new(&program, &entries, &mut native_host, limits)
4433 .run()
4434 .expect("native engine runs");
4435
4436 assert_eq!(
4437 interpreter.value, native.value,
4438 "return value parity: interpreter {:?} vs native {:?}",
4439 interpreter.value, native.value
4440 );
4441 assert_eq!(
4442 interpreter.outcome, native.outcome,
4443 "outcome parity: interpreter {:?} vs native {:?}",
4444 interpreter.outcome, native.outcome
4445 );
4446 assert_eq!(
4447 interpreter.entry_registers, native.entry_registers,
4448 "entry register parity"
4449 );
4450 native.value
4451 }
4452
4453 fn trivial_program() -> Program<Verified> {
4454 let code = verified(
4455 vec![Constant::String(EcmaString::from_utf8("<test>"))],
4456 vec![entry_function(1, vec![Instruction::Halt])],
4457 );
4458 Program::link(
4459 vec![ProgramModule {
4460 name: ConstantId::new(0),
4461 code,
4462 edges: Vec::new(),
4463 bindings: Vec::new(),
4464 exports: Vec::new(),
4465 }],
4466 ModuleId::new(0),
4467 )
4468 .expect("valid native test program")
4469 }
4470
4471 #[test]
4472 fn linked_backend_accepts_metadata_empty_single_module_program() {
4473 let program = trivial_program();
4474 let entries = SmokeEntries {
4475 program_bytes: program.encode(),
4476 invoked: Cell::new(None),
4477 };
4478 let mut host = SilentHost;
4479 let outcome = run_linked_program(&program, &entries, &mut host, &Limits::default())
4480 .expect("linked program runs");
4481 assert_eq!(entries.invoked.get(), Some(0), "entry function 0 invoked");
4482 assert_eq!(outcome.exit_code, 0);
4483 assert!(outcome.stdout.is_empty());
4484 }
4485
4486 #[test]
4487 fn linked_backend_propagates_abi_error() {
4488 let program = trivial_program();
4489 let entries = FailingEntries {
4490 program_bytes: program.encode(),
4491 };
4492 let mut host = SilentHost;
4493 let error = run_linked_program(&program, &entries, &mut host, &Limits::default())
4494 .expect_err("entry table failure surfaces");
4495 assert!(matches!(
4496 error,
4497 NativeError::Abi(AbiError::UnknownFunction {
4498 module_id: 0,
4499 function_id: 0
4500 })
4501 ));
4502 }
4503
4504 #[test]
4505 fn linked_backend_rejects_entries_from_same_shape_different_program_before_invocation() {
4506 let program = |constant| {
4507 linked(
4508 vec![program_module(
4509 "entry",
4510 vec![Constant::Int32(constant)],
4511 vec![entry_function(
4512 1,
4513 vec![
4514 Instruction::LoadConst {
4515 dst: reg(0),
4516 constant: cid(1),
4517 },
4518 Instruction::Return { value: reg(0) },
4519 ],
4520 )],
4521 Vec::new(),
4522 Vec::new(),
4523 Vec::new(),
4524 )],
4525 0,
4526 )
4527 };
4528 let compiled_program = program(1);
4529 let supplied_program = program(2);
4530 let entries = ForeignEntries {
4531 program_bytes: compiled_program.encode(),
4532 invoked: Cell::new(false),
4533 };
4534 let mut host = SilentHost;
4535
4536 assert_eq!(
4537 run_linked_program(&supplied_program, &entries, &mut host, &Limits::default()),
4538 Err(NativeError::ProgramMismatch)
4539 );
4540 assert!(
4541 !entries.invoked.get(),
4542 "mismatched entries must not be invoked"
4543 );
4544 }
4545
4546 #[test]
4547 fn linked_backend_invokes_static_dependencies_before_entry_by_tuple() {
4548 let single = trivial_program();
4549 let first = single.modules()[0].clone();
4550 let second = program_module(
4551 "entry",
4552 vec![Constant::String(EcmaString::from_utf8("dependency"))],
4553 vec![entry_function(1, vec![Instruction::Halt])],
4554 vec![Edge {
4555 specifier: cid(1),
4556 target: EdgeTarget::Local(ModuleId::new(0)),
4557 kind: EdgeKind::Static,
4558 }],
4559 Vec::new(),
4560 Vec::new(),
4561 );
4562 let program = Program::link(vec![first, second], ModuleId::new(1)).unwrap();
4563 let entries = RecordingEntries {
4564 program_bytes: program.encode(),
4565 ..RecordingEntries::default()
4566 };
4567 let mut host = SilentHost;
4568 run_linked_program(&program, &entries, &mut host, &Limits::default()).unwrap();
4569 assert_eq!(entries.invoked.borrow().as_slice(), &[(0, 0), (1, 0)]);
4570 }
4571
4572 #[test]
4573 fn dynamic_import_cycle_matches_the_reference_backend() {
4574 let execution = assert_program_parity(&dynamic_cycle_program()).unwrap();
4575 assert_eq!(execution.value, Value::TRUE);
4576 assert_eq!(execution.entry_registers[0], execution.entry_registers[1]);
4577 }
4578
4579 #[test]
4580 fn dynamic_import_throw_is_caught_at_the_requester_in_both_backends() {
4581 let root = program_module(
4582 "root",
4583 vec![Constant::String(EcmaString::from_utf8("./target"))],
4584 vec![Function::new(
4585 None,
4586 0,
4587 0,
4588 2,
4589 FunctionFlags::default(),
4590 vec![
4591 Instruction::Import {
4592 dst: reg(0),
4593 specifier: cid(1),
4594 },
4595 Instruction::Halt,
4596 Instruction::Return { value: reg(1) },
4597 ],
4598 vec![ExceptionHandler {
4599 start: pc(0),
4600 end: pc(1),
4601 handler: pc(2),
4602 catch_register: reg(1),
4603 }],
4604 )],
4605 vec![Edge {
4606 specifier: cid(1),
4607 target: EdgeTarget::Local(ModuleId::new(1)),
4608 kind: EdgeKind::Dynamic,
4609 }],
4610 Vec::new(),
4611 Vec::new(),
4612 );
4613 let target = program_module(
4614 "target",
4615 vec![Constant::Int32(9)],
4616 vec![entry_function(
4617 1,
4618 vec![
4619 Instruction::LoadConst {
4620 dst: reg(0),
4621 constant: cid(1),
4622 },
4623 Instruction::Throw { value: reg(0) },
4624 ],
4625 )],
4626 Vec::new(),
4627 Vec::new(),
4628 Vec::new(),
4629 );
4630
4631 assert_eq!(
4632 assert_program_parity(&linked(vec![root, target], 0))
4633 .unwrap()
4634 .value,
4635 Value::int32(9)
4636 );
4637 }
4638
4639 #[test]
4640 fn linked_dynamic_import_invokes_the_target_once() {
4641 let program = dynamic_cycle_program();
4642 let entries = RecordingEntries {
4643 program_bytes: program.encode(),
4644 ..RecordingEntries::default()
4645 };
4646 let mut host = SilentHost;
4647 let engine = NativeEngine::build(
4648 &program,
4649 &entries,
4650 &mut host,
4651 Limits::default(),
4652 Backend::Linked,
4653 );
4654 engine.machine.borrow_mut().instantiate_modules().unwrap();
4655 assert!(matches!(
4656 engine
4657 .machine
4658 .borrow_mut()
4659 .begin_module_evaluation(ModuleId::new(0))
4660 .unwrap(),
4661 crate::ModuleEvaluation::Ready(_)
4662 ));
4663
4664 let mut registers = [Value::UNINITIALIZED; 3];
4665 let handles = registers.as_mut_ptr();
4666 let mut shadow =
4667 ShadowFrame::new(std::ptr::null_mut(), 0, 0, handles, registers.len() as u16);
4668 let mut frame = NativeFrame::new(&mut shadow, &mut registers).unwrap();
4669 let first = engine.dispatch(&mut frame, HelperCall::Import { specifier: 1 });
4670 let second = engine.dispatch(&mut frame, HelperCall::Import { specifier: 1 });
4671
4672 assert_eq!(first.tag, CompletionTag::Normal);
4673 assert_eq!(second.tag, CompletionTag::Normal);
4674 assert_eq!(first.value, second.value);
4675 assert_eq!(entries.invoked.borrow().as_slice(), &[(1, 0)]);
4676 }
4677
4678 #[test]
4679 fn native_functions_call_and_construct_with_engine_parity() {
4680 let module = verified(
4681 vec![
4682 Constant::String(EcmaString::from_utf8("Object")),
4683 Constant::String(EcmaString::from_utf8("prototype")),
4684 Constant::String(EcmaString::from_utf8("toString")),
4685 Constant::String(EcmaString::from_utf8("call")),
4686 Constant::String(EcmaString::from_utf8("[object Object]")),
4687 Constant::Undefined,
4688 ],
4689 vec![entry_function(
4690 8,
4691 vec![
4692 Instruction::LoadGlobal {
4693 dst: reg(0),
4694 name: cid(0),
4695 },
4696 Instruction::CreateArray { dst: reg(1) },
4697 Instruction::Construct {
4698 dst: reg(2),
4699 callee: reg(0),
4700 arguments: reg(1),
4701 },
4702 Instruction::LoadConst {
4703 dst: reg(3),
4704 constant: cid(1),
4705 },
4706 Instruction::GetProperty {
4707 dst: reg(4),
4708 object: reg(0),
4709 key: reg(3),
4710 },
4711 Instruction::LoadConst {
4712 dst: reg(3),
4713 constant: cid(2),
4714 },
4715 Instruction::GetProperty {
4716 dst: reg(4),
4717 object: reg(4),
4718 key: reg(3),
4719 },
4720 Instruction::LoadConst {
4721 dst: reg(3),
4722 constant: cid(3),
4723 },
4724 Instruction::GetProperty {
4725 dst: reg(5),
4726 object: reg(4),
4727 key: reg(3),
4728 },
4729 Instruction::CreateArray { dst: reg(6) },
4730 Instruction::ArrayPush {
4731 array: reg(6),
4732 value: reg(2),
4733 },
4734 Instruction::Call {
4735 dst: reg(5),
4736 callee: reg(5),
4737 this_value: reg(4),
4738 arguments: reg(6),
4739 },
4740 Instruction::LoadConst {
4741 dst: reg(6),
4742 constant: cid(4),
4743 },
4744 Instruction::Binary {
4745 dst: reg(7),
4746 op: BinaryOp::StrictEqual,
4747 left: reg(5),
4748 right: reg(6),
4749 },
4750 Instruction::Return { value: reg(7) },
4751 ],
4752 )],
4753 );
4754 assert_eq!(assert_parity(&module, || SilentHost), Value::TRUE);
4755 }
4756
4757 #[test]
4758 fn builtin_iterators_match_between_engines() {
4759 let module = verified(
4760 vec![Constant::Int32(8)],
4761 vec![entry_function(
4762 5,
4763 vec![
4764 Instruction::CreateArray { dst: reg(0) },
4765 Instruction::LoadConst {
4766 dst: reg(1),
4767 constant: cid(0),
4768 },
4769 Instruction::ArrayPush {
4770 array: reg(0),
4771 value: reg(1),
4772 },
4773 Instruction::GetIterator {
4774 dst: reg(2),
4775 src: reg(0),
4776 kind: IteratorKind::Sync,
4777 },
4778 Instruction::IteratorNext {
4779 done: reg(3),
4780 value: reg(4),
4781 iterator: reg(2),
4782 },
4783 Instruction::Return { value: reg(4) },
4784 ],
4785 )],
4786 );
4787
4788 assert_eq!(assert_parity(&module, || SilentHost), Value::int32(8));
4789 }
4790
4791 #[test]
4792 fn bound_calls_match_between_engines() {
4793 let module = verified(
4794 vec![
4795 Constant::String(EcmaString::from_utf8("bind")),
4796 Constant::String(EcmaString::from_utf8("marker")),
4797 Constant::String(EcmaString::from_utf8("0")),
4798 Constant::String(EcmaString::from_utf8("1")),
4799 Constant::Int32(7),
4800 Constant::Int32(1),
4801 Constant::Int32(2),
4802 ],
4803 vec![
4804 entry_function(
4805 8,
4806 vec![
4807 Instruction::CreateArray { dst: reg(0) },
4808 Instruction::CreateClosure {
4809 dst: reg(0),
4810 function: FunctionId::new(1),
4811 captures: reg(0),
4812 },
4813 Instruction::LoadConst {
4814 dst: reg(1),
4815 constant: cid(0),
4816 },
4817 Instruction::GetProperty {
4818 dst: reg(2),
4819 object: reg(0),
4820 key: reg(1),
4821 },
4822 Instruction::CreateObject { dst: reg(3) },
4823 Instruction::LoadConst {
4824 dst: reg(4),
4825 constant: cid(1),
4826 },
4827 Instruction::LoadConst {
4828 dst: reg(6),
4829 constant: cid(4),
4830 },
4831 Instruction::SetProperty {
4832 object: reg(3),
4833 key: reg(4),
4834 value: reg(6),
4835 },
4836 Instruction::CreateArray { dst: reg(5) },
4837 Instruction::ArrayPush {
4838 array: reg(5),
4839 value: reg(3),
4840 },
4841 Instruction::LoadConst {
4842 dst: reg(6),
4843 constant: cid(5),
4844 },
4845 Instruction::ArrayPush {
4846 array: reg(5),
4847 value: reg(6),
4848 },
4849 Instruction::Call {
4850 dst: reg(7),
4851 callee: reg(2),
4852 this_value: reg(0),
4853 arguments: reg(5),
4854 },
4855 Instruction::CreateArray { dst: reg(5) },
4856 Instruction::LoadConst {
4857 dst: reg(6),
4858 constant: cid(6),
4859 },
4860 Instruction::ArrayPush {
4861 array: reg(5),
4862 value: reg(6),
4863 },
4864 Instruction::Call {
4865 dst: reg(7),
4866 callee: reg(7),
4867 this_value: reg(3),
4868 arguments: reg(5),
4869 },
4870 Instruction::Return { value: reg(7) },
4871 ],
4872 ),
4873 receiver_sum_function(),
4874 ],
4875 );
4876
4877 assert_eq!(
4878 assert_parity(&module, || SilentHost),
4879 crate::number_value(10.0)
4880 );
4881 }
4882
4883 #[test]
4884 fn applied_calls_match_between_engines() {
4885 let module = verified(
4886 vec![
4887 Constant::String(EcmaString::from_utf8("apply")),
4888 Constant::String(EcmaString::from_utf8("marker")),
4889 Constant::String(EcmaString::from_utf8("0")),
4890 Constant::String(EcmaString::from_utf8("1")),
4891 Constant::Int32(7),
4892 Constant::Int32(1),
4893 Constant::Int32(2),
4894 Constant::String(EcmaString::from_utf8("length")),
4895 Constant::Undefined,
4896 ],
4897 vec![
4898 entry_function(
4899 8,
4900 vec![
4901 Instruction::CreateArray { dst: reg(0) },
4902 Instruction::CreateClosure {
4903 dst: reg(0),
4904 function: FunctionId::new(1),
4905 captures: reg(0),
4906 },
4907 Instruction::LoadConst {
4908 dst: reg(1),
4909 constant: cid(0),
4910 },
4911 Instruction::GetProperty {
4912 dst: reg(2),
4913 object: reg(0),
4914 key: reg(1),
4915 },
4916 Instruction::CreateObject { dst: reg(3) },
4917 Instruction::LoadConst {
4918 dst: reg(4),
4919 constant: cid(1),
4920 },
4921 Instruction::LoadConst {
4922 dst: reg(6),
4923 constant: cid(4),
4924 },
4925 Instruction::SetProperty {
4926 object: reg(3),
4927 key: reg(4),
4928 value: reg(6),
4929 },
4930 Instruction::CreateArray { dst: reg(5) },
4931 Instruction::LoadConst {
4932 dst: reg(6),
4933 constant: cid(5),
4934 },
4935 Instruction::ArrayPush {
4936 array: reg(5),
4937 value: reg(6),
4938 },
4939 Instruction::LoadConst {
4940 dst: reg(6),
4941 constant: cid(6),
4942 },
4943 Instruction::ArrayPush {
4944 array: reg(5),
4945 value: reg(6),
4946 },
4947 Instruction::CreateArray { dst: reg(6) },
4948 Instruction::ArrayPush {
4949 array: reg(6),
4950 value: reg(3),
4951 },
4952 Instruction::ArrayPush {
4953 array: reg(6),
4954 value: reg(5),
4955 },
4956 Instruction::Call {
4957 dst: reg(7),
4958 callee: reg(2),
4959 this_value: reg(0),
4960 arguments: reg(6),
4961 },
4962 Instruction::CreateArray { dst: reg(0) },
4963 Instruction::CreateClosure {
4964 dst: reg(0),
4965 function: FunctionId::new(2),
4966 captures: reg(0),
4967 },
4968 Instruction::GetProperty {
4969 dst: reg(2),
4970 object: reg(0),
4971 key: reg(1),
4972 },
4973 Instruction::CreateArray { dst: reg(6) },
4974 Instruction::ArrayPush {
4975 array: reg(6),
4976 value: reg(3),
4977 },
4978 Instruction::LoadConst {
4979 dst: reg(4),
4980 constant: cid(8),
4981 },
4982 Instruction::ArrayPush {
4983 array: reg(6),
4984 value: reg(4),
4985 },
4986 Instruction::Call {
4987 dst: reg(4),
4988 callee: reg(2),
4989 this_value: reg(0),
4990 arguments: reg(6),
4991 },
4992 Instruction::Binary {
4993 dst: reg(7),
4994 op: BinaryOp::Add,
4995 left: reg(7),
4996 right: reg(4),
4997 },
4998 Instruction::Return { value: reg(7) },
4999 ],
5000 ),
5001 receiver_sum_function(),
5002 module_function(
5003 0,
5004 3,
5005 vec![
5006 Instruction::LoadArguments { dst: reg(0) },
5007 Instruction::LoadConst {
5008 dst: reg(1),
5009 constant: cid(7),
5010 },
5011 Instruction::GetProperty {
5012 dst: reg(2),
5013 object: reg(0),
5014 key: reg(1),
5015 },
5016 Instruction::Return { value: reg(2) },
5017 ],
5018 ),
5019 ],
5020 );
5021
5022 assert_eq!(
5023 assert_parity(&module, || SilentHost),
5024 crate::number_value(10.0)
5025 );
5026 }
5027
5028 #[test]
5029 fn bound_construction_matches_between_engines() {
5030 let module = verified(
5031 vec![
5032 Constant::String(EcmaString::from_utf8("bind")),
5033 Constant::String(EcmaString::from_utf8("prototype")),
5034 Constant::String(EcmaString::from_utf8("sum")),
5035 Constant::String(EcmaString::from_utf8("0")),
5036 Constant::String(EcmaString::from_utf8("1")),
5037 Constant::Int32(4),
5038 Constant::Int32(5),
5039 Constant::Int32(9),
5040 Constant::Undefined,
5041 ],
5042 vec![
5043 entry_function(
5044 10,
5045 vec![
5046 Instruction::CreateArray { dst: reg(0) },
5047 Instruction::CreateClosure {
5048 dst: reg(0),
5049 function: FunctionId::new(1),
5050 captures: reg(0),
5051 },
5052 Instruction::CreateObject { dst: reg(2) },
5053 Instruction::LoadConst {
5054 dst: reg(1),
5055 constant: cid(1),
5056 },
5057 Instruction::SetProperty {
5058 object: reg(0),
5059 key: reg(1),
5060 value: reg(2),
5061 },
5062 Instruction::LoadConst {
5063 dst: reg(1),
5064 constant: cid(0),
5065 },
5066 Instruction::GetProperty {
5067 dst: reg(3),
5068 object: reg(0),
5069 key: reg(1),
5070 },
5071 Instruction::CreateObject { dst: reg(4) },
5072 Instruction::CreateArray { dst: reg(5) },
5073 Instruction::ArrayPush {
5074 array: reg(5),
5075 value: reg(4),
5076 },
5077 Instruction::LoadConst {
5078 dst: reg(6),
5079 constant: cid(5),
5080 },
5081 Instruction::ArrayPush {
5082 array: reg(5),
5083 value: reg(6),
5084 },
5085 Instruction::Call {
5086 dst: reg(7),
5087 callee: reg(3),
5088 this_value: reg(0),
5089 arguments: reg(5),
5090 },
5091 Instruction::CreateArray { dst: reg(5) },
5092 Instruction::LoadConst {
5093 dst: reg(6),
5094 constant: cid(6),
5095 },
5096 Instruction::ArrayPush {
5097 array: reg(5),
5098 value: reg(6),
5099 },
5100 Instruction::Construct {
5101 dst: reg(8),
5102 callee: reg(7),
5103 arguments: reg(5),
5104 },
5105 Instruction::LoadConst {
5106 dst: reg(1),
5107 constant: cid(2),
5108 },
5109 Instruction::GetProperty {
5110 dst: reg(9),
5111 object: reg(8),
5112 key: reg(1),
5113 },
5114 Instruction::LoadConst {
5115 dst: reg(6),
5116 constant: cid(7),
5117 },
5118 Instruction::Binary {
5119 dst: reg(9),
5120 op: BinaryOp::StrictEqual,
5121 left: reg(9),
5122 right: reg(6),
5123 },
5124 Instruction::Binary {
5125 dst: reg(6),
5126 op: BinaryOp::InstanceOf,
5127 left: reg(8),
5128 right: reg(7),
5129 },
5130 Instruction::Binary {
5131 dst: reg(9),
5132 op: BinaryOp::BitAnd,
5133 left: reg(9),
5134 right: reg(6),
5135 },
5136 Instruction::Return { value: reg(9) },
5137 ],
5138 ),
5139 module_function(
5140 0,
5141 6,
5142 vec![
5143 Instruction::LoadThis { dst: reg(0) },
5144 Instruction::LoadArguments { dst: reg(1) },
5145 Instruction::LoadConst {
5146 dst: reg(2),
5147 constant: cid(3),
5148 },
5149 Instruction::GetProperty {
5150 dst: reg(3),
5151 object: reg(1),
5152 key: reg(2),
5153 },
5154 Instruction::LoadConst {
5155 dst: reg(2),
5156 constant: cid(4),
5157 },
5158 Instruction::GetProperty {
5159 dst: reg(4),
5160 object: reg(1),
5161 key: reg(2),
5162 },
5163 Instruction::Binary {
5164 dst: reg(3),
5165 op: BinaryOp::Add,
5166 left: reg(3),
5167 right: reg(4),
5168 },
5169 Instruction::LoadConst {
5170 dst: reg(2),
5171 constant: cid(2),
5172 },
5173 Instruction::SetProperty {
5174 object: reg(0),
5175 key: reg(2),
5176 value: reg(3),
5177 },
5178 Instruction::LoadConst {
5179 dst: reg(5),
5180 constant: cid(8),
5181 },
5182 Instruction::Return { value: reg(5) },
5183 ],
5184 ),
5185 ],
5186 );
5187
5188 assert_eq!(assert_parity(&module, || SilentHost), Value::int32(1));
5189 }
5190
5191 #[test]
5192 fn linked_entry_preserves_pending_throw_origin() {
5193 let program = trivial_program();
5194 let mut host = SilentHost;
5195 let mut engine = NativeEngine::build(
5196 &program,
5197 &ThrowEntries,
5198 &mut host,
5199 Limits::default(),
5200 Backend::Linked,
5201 );
5202 engine.pending_throw.set(Some(PendingThrow {
5203 value: Value::UNDEFINED,
5204 origin: ThrowOrigin::ReferenceError {
5205 operation: "fixture",
5206 },
5207 }));
5208 let error = engine.run_linked().unwrap_err();
5209 assert!(matches!(
5210 error,
5211 NativeError::Runtime(RuntimeError {
5212 kind: RuntimeErrorKind::UncaughtThrow {
5213 origin: ThrowOrigin::ReferenceError {
5214 operation: "fixture"
5215 },
5216 ..
5217 },
5218 ..
5219 })
5220 ));
5221 }
5222
5223 #[test]
5224 fn stale_pending_throw_cannot_replace_a_new_bytecode_throw() {
5225 let program = trivial_program();
5226 let mut host = SilentHost;
5227 let engine = NativeEngine::build(
5228 &program,
5229 &NoEntries,
5230 &mut host,
5231 Limits::default(),
5232 Backend::Linked,
5233 );
5234 engine.pending_throw.set(Some(PendingThrow {
5235 value: Value::int32(1),
5236 origin: ThrowOrigin::ReferenceError { operation: "stale" },
5237 }));
5238
5239 assert_eq!(
5240 engine.take_matching_throw(Value::int32(2)),
5241 (Value::int32(2), ThrowOrigin::Bytecode)
5242 );
5243 assert!(engine.pending_throw.get().is_none());
5244 }
5245
5246 #[test]
5247 fn linked_entry_sources_pending_runtime_fatal_kind() {
5248 let program = trivial_program();
5249 let mut host = SilentHost;
5250 let mut engine = NativeEngine::build(
5251 &program,
5252 &FatalEntries,
5253 &mut host,
5254 Limits::default(),
5255 Backend::Linked,
5256 );
5257 engine
5258 .pending_fatal_kind
5259 .set(Some(RuntimeErrorKind::InvalidValue { value: Value::NULL }));
5260 let error = engine.run_linked().unwrap_err();
5261 assert!(matches!(
5262 error,
5263 NativeError::Runtime(RuntimeError {
5264 kind: RuntimeErrorKind::InvalidValue { value },
5265 ..
5266 }) if value == Value::NULL
5267 ));
5268 }
5269
5270 #[test]
5271 fn nested_linked_unknown_tuple_remains_abi_error() {
5272 let module = verified(
5273 Vec::new(),
5274 vec![
5275 entry_function(1, vec![Instruction::Halt]),
5276 entry_function(1, vec![Instruction::Halt]),
5277 ],
5278 );
5279 let program = one_module_program(&module);
5280 let entries = FailingEntries::default();
5281 let mut host = SilentHost;
5282 let engine = NativeEngine::build(
5283 &program,
5284 &entries,
5285 &mut host,
5286 Limits::default(),
5287 Backend::Linked,
5288 );
5289 let outcome = engine.invoke_runtime(
5290 crate::RuntimeFunction {
5291 module: ModuleId::new(0),
5292 function: FunctionId::new(1),
5293 },
5294 &[],
5295 Value::UNDEFINED,
5296 Value::UNDEFINED,
5297 &[],
5298 );
5299 assert!(matches!(outcome, InvokeOutcome::Fatal));
5300 assert!(matches!(
5301 engine.pending_abi_error.take(),
5302 Some(AbiError::UnknownFunction {
5303 module_id: 0,
5304 function_id: 1
5305 })
5306 ));
5307 }
5308
5309 #[test]
5310 fn linked_abi_failure_leaves_module_retryable() {
5311 let program = trivial_program();
5312 let entries = FailingEntries::default();
5313 let mut host = SilentHost;
5314 let mut engine = NativeEngine::build(
5315 &program,
5316 &entries,
5317 &mut host,
5318 Limits::default(),
5319 Backend::Linked,
5320 );
5321 engine.machine.borrow_mut().instantiate_modules().unwrap();
5322 for _ in 0..2 {
5323 assert!(matches!(
5324 engine.evaluate_linked_module(ModuleId::new(0)),
5325 Err(NativeError::Abi(AbiError::UnknownFunction {
5326 module_id: 0,
5327 function_id: 0
5328 }))
5329 ));
5330 }
5331 }
5332
5333 #[test]
5334 fn linked_backend_routes_dynamic_targets_away_from_entry_table() {
5335 let root = one_module_program(&verified(
5336 Vec::new(),
5337 vec![entry_function(1, vec![Instruction::Halt])],
5338 ));
5339 let script = Arc::new(one_module_program(&verified(
5340 vec![Constant::Int32(42)],
5341 vec![entry_function(
5342 1,
5343 vec![
5344 Instruction::LoadConst {
5345 dst: reg(0),
5346 constant: cid(0),
5347 },
5348 Instruction::Return { value: reg(0) },
5349 ],
5350 )],
5351 )));
5352 let entries = RecordingEntries::default();
5353 let mut host = SilentHost;
5354 let engine = NativeEngine::build(
5355 &root,
5356 &entries,
5357 &mut host,
5358 Limits::default(),
5359 Backend::Linked,
5360 );
5361 engine.machine.borrow_mut().instantiate_modules().unwrap();
5362 let module = engine
5363 .machine
5364 .borrow_mut()
5365 .install_script_reserving(script, 0, 0)
5366 .unwrap();
5367
5368 let outcome = engine.invoke_runtime(
5369 crate::RuntimeFunction {
5370 module,
5371 function: FunctionId::new(0),
5372 },
5373 &[],
5374 Value::UNDEFINED,
5375 Value::UNDEFINED,
5376 &[],
5377 );
5378 assert!(matches!(outcome, InvokeOutcome::Value(value) if value == Value::int32(42)));
5379 assert!(entries.invoked.borrow().is_empty());
5380 }
5381 fn generator_program(code: Vec<Instruction>, constants: Vec<Constant>) -> Program<Verified> {
5382 let generator = Function::new(
5383 None,
5384 0,
5385 0,
5386 3,
5387 FunctionFlags {
5388 is_async: false,
5389 is_generator: true,
5390 },
5391 code,
5392 Vec::new(),
5393 );
5394 one_module_program(&verified(
5395 constants,
5396 vec![entry_function(1, vec![Instruction::Halt]), generator],
5397 ))
5398 }
5399
5400 fn yielding_generator_program() -> Program<Verified> {
5401 generator_program(
5402 vec![
5403 Instruction::LoadConst {
5404 dst: reg(0),
5405 constant: cid(0),
5406 },
5407 Instruction::Suspend {
5408 dst: reg(1),
5409 src: reg(0),
5410 resume: pc(2),
5411 },
5412 Instruction::LoadConst {
5413 dst: reg(0),
5414 constant: cid(1),
5415 },
5416 Instruction::Suspend {
5417 dst: reg(1),
5418 src: reg(0),
5419 resume: pc(4),
5420 },
5421 Instruction::Return { value: reg(1) },
5422 ],
5423 vec![Constant::Int32(4), Constant::Int32(5)],
5424 )
5425 }
5426
5427 fn invoke_test_generator<H: Host>(engine: &NativeEngine<'_, '_, H>) -> Value {
5428 match engine.invoke_runtime(
5429 crate::RuntimeFunction {
5430 module: ModuleId::new(0),
5431 function: FunctionId::new(1),
5432 },
5433 &[],
5434 Value::UNDEFINED,
5435 Value::UNDEFINED,
5436 &[],
5437 ) {
5438 InvokeOutcome::Value(generator) => generator,
5439 _ => panic!("generator call must return its lazy generator object"),
5440 }
5441 }
5442
5443 fn assert_iterator_result<H: Host>(
5444 engine: &NativeEngine<'_, '_, H>,
5445 outcome: InvokeOutcome,
5446 expected_value: Value,
5447 expected_done: bool,
5448 ) {
5449 let result = match outcome {
5450 InvokeOutcome::Value(result) => result,
5451 _ => panic!("generator next must return an iterator result"),
5452 };
5453 let mut machine = engine.machine.borrow_mut();
5454 let value = machine
5455 .get_named_property(result, "value")
5456 .expect("iterator result has value");
5457 let done = machine
5458 .get_named_property(result, "done")
5459 .expect("iterator result has done");
5460 assert_eq!(value, expected_value);
5461 assert_eq!(done, Value::boolean(expected_done));
5462 }
5463
5464 #[test]
5465 fn reference_generator_is_lazy_yields_resumes_and_completes_stickily() {
5466 let program = yielding_generator_program();
5467 let mut host = SilentHost;
5468 let entries = NoEntries;
5469 let engine = NativeEngine::new(&program, &entries, &mut host, Limits::default());
5470 let generator = invoke_test_generator(&engine);
5471
5472 {
5473 let machine = engine.machine.borrow();
5474 let index = machine.runtime_slot(generator).unwrap().unwrap();
5475 assert!(matches!(
5476 &machine.heap[index],
5477 HeapEntry::Generator {
5478 state: GeneratorState::SuspendedStart(_),
5479 ..
5480 }
5481 ));
5482 }
5483
5484 assert_iterator_result(
5485 &engine,
5486 engine.resume_generator(generator, Value::int32(999)),
5487 Value::int32(4),
5488 false,
5489 );
5490 {
5491 let machine = engine.machine.borrow();
5492 let index = machine.runtime_slot(generator).unwrap().unwrap();
5493 let HeapEntry::Generator {
5494 state: GeneratorState::Suspended(activation),
5495 ..
5496 } = &machine.heap[index]
5497 else {
5498 panic!("generator must retain its suspended activation");
5499 };
5500 assert_eq!(activation.resume_token, 2);
5501 assert_eq!(activation.registers[0], Value::int32(4));
5502 }
5503
5504 assert_iterator_result(
5505 &engine,
5506 engine.resume_generator(generator, Value::int32(99)),
5507 Value::int32(5),
5508 false,
5509 );
5510 assert_iterator_result(
5511 &engine,
5512 engine.resume_generator(generator, Value::int32(7)),
5513 Value::int32(7),
5514 true,
5515 );
5516 assert_iterator_result(
5517 &engine,
5518 engine.resume_generator(generator, Value::int32(8)),
5519 Value::UNDEFINED,
5520 true,
5521 );
5522 let machine = engine.machine.borrow();
5523 assert_eq!(machine.live_registers, 0);
5524 assert_eq!(machine.native_depth, 0);
5525 }
5526
5527 #[test]
5528 fn reference_generator_throw_completes_and_preserves_value_and_origin() {
5529 let program = generator_program(
5530 vec![
5531 Instruction::LoadConst {
5532 dst: reg(0),
5533 constant: cid(0),
5534 },
5535 Instruction::Throw { value: reg(0) },
5536 ],
5537 vec![Constant::Int32(42)],
5538 );
5539 let mut host = SilentHost;
5540 let entries = NoEntries;
5541 let engine = NativeEngine::new(&program, &entries, &mut host, Limits::default());
5542 let generator = invoke_test_generator(&engine);
5543 assert!(matches!(
5544 engine.resume_generator(generator, Value::UNDEFINED),
5545 InvokeOutcome::Threw(value, ThrowOrigin::Bytecode) if value == Value::int32(42)
5546 ));
5547 assert_iterator_result(
5548 &engine,
5549 engine.resume_generator(generator, Value::UNDEFINED),
5550 Value::UNDEFINED,
5551 true,
5552 );
5553 let machine = engine.machine.borrow();
5554 assert_eq!(machine.live_registers, 0);
5555 assert_eq!(machine.native_depth, 0);
5556 }
5557
5558 #[test]
5559 fn suspended_reference_generator_registers_enforce_the_global_ceiling() {
5560 let program = yielding_generator_program();
5561 let mut host = SilentHost;
5562 let entries = NoEntries;
5563 let mut limits = Limits::default();
5564 limits.max_total_registers = 3;
5565 let engine = NativeEngine::new(&program, &entries, &mut host, limits);
5566 let first = invoke_test_generator(&engine);
5567 let second = invoke_test_generator(&engine);
5568 assert_iterator_result(
5569 &engine,
5570 engine.resume_generator(first, Value::UNDEFINED),
5571 Value::int32(4),
5572 false,
5573 );
5574 assert!(matches!(
5575 engine.resume_generator(second, Value::UNDEFINED),
5576 InvokeOutcome::Fatal
5577 ));
5578 assert!(matches!(
5579 engine.pending_fatal_kind.take(),
5580 Some(RuntimeErrorKind::RegisterLimitExceeded { limit: 3 })
5581 ));
5582 }
5583
5584 #[derive(Clone, Copy)]
5585 struct GeneratorEntryStep {
5586 token: u32,
5587 next_token: u32,
5588 tag: CompletionTag,
5589 value: Value,
5590 }
5591
5592 struct GeneratorEntries {
5593 steps: Vec<GeneratorEntryStep>,
5594 call: Cell<usize>,
5595 handles: Cell<Option<*mut Value>>,
5596 }
5597
5598 impl GeneratorEntries {
5599 fn yielding() -> Self {
5600 Self {
5601 steps: vec![
5602 GeneratorEntryStep {
5603 token: 0,
5604 next_token: 2,
5605 tag: CompletionTag::Suspend,
5606 value: Value::int32(4),
5607 },
5608 GeneratorEntryStep {
5609 token: 2,
5610 next_token: 4,
5611 tag: CompletionTag::Suspend,
5612 value: Value::int32(5),
5613 },
5614 GeneratorEntryStep {
5615 token: 4,
5616 next_token: 4,
5617 tag: CompletionTag::Normal,
5618 value: Value::int32(7),
5619 },
5620 ],
5621 call: Cell::new(0),
5622 handles: Cell::new(None),
5623 }
5624 }
5625
5626 fn terminal(tag: CompletionTag, value: Value) -> Self {
5627 Self {
5628 steps: vec![GeneratorEntryStep {
5629 token: 0,
5630 next_token: 1,
5631 tag,
5632 value,
5633 }],
5634 call: Cell::new(0),
5635 handles: Cell::new(None),
5636 }
5637 }
5638 }
5639
5640 impl NativeEntryTable for GeneratorEntries {
5641 fn program_bytes(&self) -> &[u8] {
5642 &[]
5643 }
5644
5645 fn invoke(
5646 &self,
5647 module_id: u32,
5648 function_id: u32,
5649 frame: &mut ShadowFrame,
5650 out: &mut Completion,
5651 ) -> Result<CompletionTag, AbiError> {
5652 assert_eq!(module_id, 0);
5653 assert_eq!(function_id, 1);
5654 let call = self.call.get();
5655 let step = self.steps[call];
5656 self.call.set(call + 1);
5657 assert_eq!(frame.bytecode_pc, step.token);
5658 match self.handles.get() {
5659 Some(handles) => assert_eq!(
5660 handles, frame.handles,
5661 "linked resumes must reuse the saved register allocation"
5662 ),
5663 None => self.handles.set(Some(frame.handles)),
5664 }
5665 frame.bytecode_pc = step.next_token;
5666 *out = Completion::new(step.value);
5667 Ok(step.tag)
5668 }
5669 }
5670
5671 #[test]
5672 fn linked_generator_reuses_saved_registers_and_dispatches_resume_tokens() {
5673 let program = yielding_generator_program();
5674 let entries = GeneratorEntries::yielding();
5675 let mut host = SilentHost;
5676 let engine = NativeEngine::build(
5677 &program,
5678 &entries,
5679 &mut host,
5680 Limits::default(),
5681 Backend::Linked,
5682 );
5683 let generator = invoke_test_generator(&engine);
5684 assert_eq!(entries.call.get(), 0, "generator call is lazy");
5685 assert_iterator_result(
5686 &engine,
5687 engine.resume_generator(generator, Value::int32(999)),
5688 Value::int32(4),
5689 false,
5690 );
5691 assert_iterator_result(
5692 &engine,
5693 engine.resume_generator(generator, Value::int32(99)),
5694 Value::int32(5),
5695 false,
5696 );
5697 assert_iterator_result(
5698 &engine,
5699 engine.resume_generator(generator, Value::int32(7)),
5700 Value::int32(7),
5701 true,
5702 );
5703 assert_iterator_result(
5704 &engine,
5705 engine.resume_generator(generator, Value::UNDEFINED),
5706 Value::UNDEFINED,
5707 true,
5708 );
5709 assert_eq!(entries.call.get(), 3);
5710 let machine = engine.machine.borrow();
5711 assert_eq!(machine.live_registers, 0);
5712 assert_eq!(machine.native_depth, 0);
5713 }
5714
5715 #[test]
5716 fn linked_resume_value_dispatch_consumes_the_sent_value_once() {
5717 let program = yielding_generator_program();
5718 let entries = NoEntries;
5719 let mut host = SilentHost;
5720 let engine = NativeEngine::build(
5721 &program,
5722 &entries,
5723 &mut host,
5724 Limits::default(),
5725 Backend::Linked,
5726 );
5727 engine.activations.borrow_mut().push(Activation {
5728 this_value: Value::UNDEFINED,
5729 new_target: Value::UNDEFINED,
5730 args: Vec::new(),
5731 arguments_object: None,
5732 pending_resume: Some(Value::int32(77)),
5733 });
5734 let mut registers = vec![Value::UNINITIALIZED];
5735 let mut shadow = ShadowFrame::new(std::ptr::null_mut(), 2, 0, registers.as_mut_ptr(), 1);
5736 let mut frame = NativeFrame::new(&mut shadow, &mut registers).unwrap();
5737 let resumed = engine.dispatch(&mut frame, HelperCall::ResumeValue);
5738 assert_eq!(resumed, HelperResult::normal(Value::int32(77)));
5739 assert_eq!(
5740 engine.dispatch(&mut frame, HelperCall::ResumeValue).tag,
5741 CompletionTag::FatalTrap
5742 );
5743 engine.activations.borrow_mut().pop();
5744 }
5745
5746 #[test]
5747 fn linked_generator_throw_and_fatal_release_once_and_complete_stickily() {
5748 for (tag, expected_throw) in [
5749 (CompletionTag::Throw, true),
5750 (CompletionTag::FatalTrap, false),
5751 ] {
5752 let program = generator_program(vec![Instruction::Halt], Vec::new());
5753 let entries = GeneratorEntries::terminal(tag, Value::int32(42));
5754 let mut host = SilentHost;
5755 let engine = NativeEngine::build(
5756 &program,
5757 &entries,
5758 &mut host,
5759 Limits::default(),
5760 Backend::Linked,
5761 );
5762 let generator = invoke_test_generator(&engine);
5763 let outcome = engine.resume_generator(generator, Value::UNDEFINED);
5764 if expected_throw {
5765 assert!(matches!(
5766 outcome,
5767 InvokeOutcome::Threw(value, ThrowOrigin::Bytecode)
5768 if value == Value::int32(42)
5769 ));
5770 } else {
5771 assert!(matches!(outcome, InvokeOutcome::Fatal));
5772 }
5773 assert_iterator_result(
5774 &engine,
5775 engine.resume_generator(generator, Value::UNDEFINED),
5776 Value::UNDEFINED,
5777 true,
5778 );
5779 let machine = engine.machine.borrow();
5780 assert_eq!(machine.live_registers, 0);
5781 assert_eq!(machine.native_depth, 0);
5782 }
5783 }
5784
5785 #[test]
5786 fn linked_array_extend_drives_generator_through_linked_entries() {
5787 let program = yielding_generator_program();
5788 let entries = GeneratorEntries::yielding();
5789 let mut host = SilentHost;
5790 let engine = NativeEngine::build(
5791 &program,
5792 &entries,
5793 &mut host,
5794 Limits::default(),
5795 Backend::Linked,
5796 );
5797 let generator = invoke_test_generator(&engine);
5798 let array = {
5799 let prototype = engine.machine.borrow().intrinsics.array_prototype;
5800 engine
5801 .machine
5802 .borrow_mut()
5803 .allocate(HeapEntry::Array {
5804 elements: Vec::new(),
5805 properties: PropertyMap::default(),
5806 prototype: Some(prototype),
5807 extensible: true,
5808 length_writable: true,
5809 })
5810 .unwrap()
5811 };
5812 assert!(matches!(
5813 engine.array_extend_active(array, generator),
5814 InvokeOutcome::Value(value) if value == Value::UNDEFINED
5815 ));
5816 assert_eq!(
5817 engine.machine.borrow().arguments_from_array(array).unwrap(),
5818 vec![Value::int32(4), Value::int32(5)]
5819 );
5820 assert_eq!(entries.call.get(), 3);
5821 }
5822 #[test]
5823 fn create_cell_helper_seeds_tdz_and_preserves_reference_error_origin() {
5824 let program = trivial_program();
5825 let entries = NoEntries;
5826 let mut host = SilentHost;
5827 let engine = NativeEngine::build(
5828 &program,
5829 &entries,
5830 &mut host,
5831 Limits::default(),
5832 Backend::Reference,
5833 );
5834 let mut registers = vec![Value::UNINITIALIZED];
5835 let mut shadow = ShadowFrame::new(std::ptr::null_mut(), 0, 0, registers.as_mut_ptr(), 1);
5836 let mut frame = NativeFrame::new(&mut shadow, &mut registers).unwrap();
5837 let created = engine.dispatch(&mut frame, HelperCall::CreateCell);
5838 assert_eq!(created.tag, CompletionTag::Normal);
5839 let read = engine.dispatch(
5840 &mut frame,
5841 HelperCall::GetProperty {
5842 object: created.value,
5843 key: Value::int32(0),
5844 },
5845 );
5846 assert_eq!(read.tag, CompletionTag::Throw);
5847 assert!(matches!(
5848 engine.pending_throw.get(),
5849 Some(PendingThrow {
5850 origin: ThrowOrigin::ReferenceError { .. },
5851 ..
5852 })
5853 ));
5854 }
5855}