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