1#![forbid(unsafe_code)]
21
22use std::borrow::Cow;
23use std::cmp::Ordering;
24use std::collections::BTreeMap;
25use std::collections::BTreeSet;
26use std::collections::VecDeque;
27use std::error::Error;
28use std::fmt;
29use std::sync::Arc;
30
31use bamts_bytecode::{
32 AccessorKind, BinaryOp, BindingId, BindingKind, Constant, ConstantId, EcmaString,
33 EcmaStringBuilder, EdgeId, EdgeTarget, Function, FunctionId, Instruction, IteratorKind, Module,
34 ModuleId, Pc, Program, ProgramModule, ResolvedExport, UnaryOp, Verified,
35};
36use bamts_native::{Decoded, SlotId, Value};
37
38mod external_modules;
39mod host_objects;
40mod intrinsics;
41mod native;
42mod vm;
43
44pub use native::{NativeEngine, NativeError, run_linked_program};
45
46const RUNTIME_HEAP_SEGMENT: u16 = 1;
47
48#[derive(Clone, Debug, Eq, PartialEq)]
50pub struct ExecutionOutcome {
51 pub stdout: Vec<u8>,
53 pub exit_code: i32,
55}
56
57#[derive(Clone, Debug, Eq, PartialEq)]
59pub struct Execution {
60 pub outcome: ExecutionOutcome,
61 pub value: Value,
63 pub link: Value,
65 pub entry_registers: Vec<Value>,
66}
67
68#[derive(Clone, Debug, Eq, PartialEq)]
70pub struct Limits {
71 pub fuel: u64,
72 pub max_call_depth: usize,
73 pub max_total_registers: usize,
74 pub max_argument_count: u32,
76 pub max_heap_slots: usize,
77 pub max_heap_bytes: usize,
78 pub max_module_cells: usize,
80 pub max_dynamic_modules: usize,
82 pub max_microtasks: usize,
84 pub max_timers: usize,
86}
87
88impl Default for Limits {
89 fn default() -> Self {
90 Self {
91 fuel: 1_000_000,
92 max_call_depth: 64,
93 max_total_registers: 1 << 20,
94 max_argument_count: 1 << 16,
95 max_heap_slots: 1 << 20,
96 max_heap_bytes: 64 << 20,
97 max_module_cells: 1 << 20,
98 max_dynamic_modules: 1 << 10,
99 max_microtasks: 1 << 20,
100 max_timers: 1 << 20,
101 }
102 }
103}
104
105pub struct ScriptSource<'a> {
108 pub source: &'a [u16],
109 pub name: &'a [u16],
110}
111
112#[derive(Clone, Debug, Eq, PartialEq)]
114pub enum ScriptCompileError {
115 IllFormedSource {
116 unit_offset: usize,
117 },
118 Syntax {
119 message: String,
120 line: u32,
121 column: u32,
122 },
123 Unsupported {
124 message: String,
125 line: u32,
126 column: u32,
127 },
128 Capacity {
129 message: String,
130 },
131}
132
133pub trait CompileProvider {
135 fn compile_script(
136 &mut self,
137 source: ScriptSource<'_>,
138 ) -> Result<Arc<Program<Verified>>, ScriptCompileError>;
139}
140
141pub trait Host {
146 fn write_stdout(&mut self, _bytes: &[u8]) {}
147
148 fn write_stderr(&mut self, _bytes: &[u8]) {}
149
150 fn exit_code(&self) -> i32 {
151 0
152 }
153
154 fn set_exit_code(&mut self, _exit_code: i32) {}
155
156 fn argv(&self) -> &[String] {
157 &[]
158 }
159
160 fn env(&self, _name: &str) -> Option<&str> {
161 None
162 }
163
164 fn set_env(&mut self, _name: &str, _value: &str) {}
165
166 fn delete_env(&mut self, _name: &str) -> bool {
167 false
168 }
169
170 fn now_ms(&mut self) -> u64 {
171 0
172 }
173
174 fn monotonic_ns(&mut self) -> u64 {
175 0
176 }
177
178 fn random(&mut self) -> f64 {
179 0.0
180 }
181
182 fn hash(&mut self, _algorithm: &str, _data: &[u8]) -> Option<Vec<u8>> {
183 None
184 }
185
186 fn script_compiler(&mut self) -> Option<&mut (dyn CompileProvider + 'static)> {
191 None
192 }
193
194 fn timers(&mut self) -> Option<&mut (dyn TimerProvider + 'static)> {
200 None
201 }
202}
203
204#[derive(Clone, Copy, Debug, Eq, PartialEq)]
210pub struct TimerWakeup {
211 pub id: u64,
212 pub deadline_ms: u64,
213}
214
215#[derive(Clone, Debug, Eq, PartialEq)]
217pub struct TimerError {
218 message: String,
219}
220
221impl TimerError {
222 #[must_use]
223 pub fn new(message: impl Into<String>) -> Self {
224 Self {
225 message: message.into(),
226 }
227 }
228
229 #[must_use]
230 pub fn message(&self) -> &str {
231 &self.message
232 }
233}
234
235impl fmt::Display for TimerError {
236 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
237 formatter.write_str(&self.message)
238 }
239}
240
241impl Error for TimerError {}
242
243pub trait TimerProvider {
249 fn schedule(&mut self, id: u64, delay_ms: u32) -> Result<u64, TimerError>;
252
253 fn cancel(&mut self, id: u64) -> Result<bool, TimerError>;
255
256 fn poll_expired(&mut self, output: &mut Vec<TimerWakeup>) -> Result<(), TimerError>;
258
259 fn wait_expired(&mut self) -> Result<Option<TimerWakeup>, TimerError>;
261
262 fn has_pending(&self) -> bool;
264}
265
266#[derive(Clone, Copy, Debug, Eq, PartialEq)]
267pub enum ThrowOrigin {
268 Bytecode,
269 TypeError { operation: &'static str },
270 RangeError { operation: &'static str },
271 ReferenceError { operation: &'static str },
272 UriError { operation: &'static str },
273}
274
275#[derive(Clone, Debug, Eq, PartialEq)]
277pub struct RuntimeSource {
278 pub function_name: Option<EcmaString>,
279 pub instruction: Instruction,
280}
281
282#[derive(Clone, Debug, Eq, PartialEq)]
283pub struct RuntimeError {
284 pub kind: RuntimeErrorKind,
285 pub function: FunctionId,
286 pub pc: Pc,
287 pub source: RuntimeSource,
288}
289
290#[derive(Clone, Debug, Eq, PartialEq)]
291pub enum RuntimeErrorKind {
292 UncaughtThrow {
293 value: Value,
294 origin: ThrowOrigin,
295 },
296 FuelExhausted {
297 limit: u64,
298 },
299 CallDepthExceeded {
300 limit: usize,
301 },
302 RegisterLimitExceeded {
303 limit: usize,
304 },
305 ArgumentLimitExceeded {
306 limit: u32,
307 requested: u32,
308 },
309 HeapSlotLimitExceeded {
310 limit: usize,
311 },
312 HeapByteLimitExceeded {
313 limit: usize,
314 },
315 ModuleCellLimitExceeded {
316 limit: usize,
317 },
318 DynamicModuleLimitExceeded {
319 limit: usize,
320 },
321 MicrotaskQueueLimitExceeded {
322 limit: usize,
323 },
324 MicrotaskDrainReentry,
325 TimerProviderFailure {
326 message: String,
327 },
328 TimerCapacityExceeded {
329 limit: usize,
330 },
331 TimerCheckpointReentry,
332 InvalidDynamicScript {
333 reason: &'static str,
334 },
335 TemporalDeadZone {
336 module: ModuleId,
337 binding: BindingId,
338 },
339 ExternalModuleUnavailable {
340 module: ModuleId,
341 edge: EdgeId,
342 },
343 DynamicImportEdgeMissing {
344 module: ModuleId,
345 specifier: ConstantId,
346 },
347 InvalidVerifiedProgram {
348 module: ModuleId,
349 instruction: Instruction,
350 },
351 InvalidValue {
352 value: Value,
353 },
354 InvalidRuntimeHeapReference {
355 slot: u32,
356 },
357}
358
359impl fmt::Display for RuntimeError {
360 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
361 write!(
362 formatter,
363 "runtime error in function {} at pc {}",
364 self.function.get(),
365 self.pc.get()
366 )?;
367 if let Some(name) = &self.source.function_name {
368 write!(formatter, " ({})", name.to_utf8_lossy())?;
369 }
370 write!(formatter, ": ")?;
371 match &self.kind {
372 RuntimeErrorKind::UncaughtThrow { value, origin } => write!(
373 formatter,
374 "uncaught {origin:?} throw (value {:#018x})",
375 value.to_bits()
376 ),
377 RuntimeErrorKind::FuelExhausted { limit } => {
378 write!(formatter, "fuel exhausted after {limit} instructions")
379 }
380 RuntimeErrorKind::CallDepthExceeded { limit } => {
381 write!(formatter, "call depth limit {limit} exceeded")
382 }
383 RuntimeErrorKind::RegisterLimitExceeded { limit } => {
384 write!(formatter, "live register limit {limit} exceeded")
385 }
386 RuntimeErrorKind::ArgumentLimitExceeded { limit, requested } => write!(
387 formatter,
388 "argument count {requested} exceeds runtime limit {limit}"
389 ),
390 RuntimeErrorKind::HeapSlotLimitExceeded { limit } => {
391 write!(formatter, "heap slot limit {limit} exceeded")
392 }
393 RuntimeErrorKind::HeapByteLimitExceeded { limit } => {
394 write!(formatter, "heap byte limit {limit} exceeded")
395 }
396 RuntimeErrorKind::ModuleCellLimitExceeded { limit } => {
397 write!(formatter, "module cell limit {limit} exceeded")
398 }
399 RuntimeErrorKind::DynamicModuleLimitExceeded { limit } => {
400 write!(formatter, "dynamic module limit {limit} exceeded")
401 }
402 RuntimeErrorKind::MicrotaskQueueLimitExceeded { limit } => {
403 write!(formatter, "microtask queue limit {limit} exceeded")
404 }
405 RuntimeErrorKind::MicrotaskDrainReentry => {
406 write!(formatter, "microtask drain is already active")
407 }
408 RuntimeErrorKind::TimerProviderFailure { message } => {
409 write!(formatter, "timer provider failure: {message}")
410 }
411 RuntimeErrorKind::TimerCapacityExceeded { limit } => {
412 write!(formatter, "timer capacity {limit} exceeded")
413 }
414 RuntimeErrorKind::TimerCheckpointReentry => {
415 write!(formatter, "timer checkpoint is already active")
416 }
417 RuntimeErrorKind::InvalidDynamicScript { reason } => {
418 write!(formatter, "invalid dynamic script: {reason}")
419 }
420 RuntimeErrorKind::TemporalDeadZone { module, binding } => write!(
421 formatter,
422 "module {} binding {} read before initialization",
423 module.get(),
424 binding.get()
425 ),
426 RuntimeErrorKind::ExternalModuleUnavailable { module, edge } => write!(
427 formatter,
428 "external module at module {} edge {} is unavailable",
429 module.get(),
430 edge.get()
431 ),
432 RuntimeErrorKind::DynamicImportEdgeMissing { module, specifier } => write!(
433 formatter,
434 "verified module {} has no dynamic edge for constant {}",
435 module.get(),
436 specifier.get()
437 ),
438 RuntimeErrorKind::InvalidVerifiedProgram {
439 module,
440 instruction,
441 } => write!(
442 formatter,
443 "verified module {} contains impossible instruction {instruction:?}",
444 module.get()
445 ),
446 RuntimeErrorKind::InvalidValue { value } => {
447 write!(
448 formatter,
449 "malformed or foreign value {:#018x}",
450 value.to_bits()
451 )
452 }
453 RuntimeErrorKind::InvalidRuntimeHeapReference { slot } => {
454 write!(formatter, "runtime heap slot {slot} does not exist")
455 }
456 }
457 }
458}
459
460impl Error for RuntimeError {}
461
462#[must_use]
465pub fn constant_value(constant: &Constant) -> Option<Value> {
466 match constant {
467 Constant::Number(bits) => Some(Value::number(bits.to_f64())),
468 Constant::Int32(value) => Some(Value::int32(*value as u32)),
469 Constant::String(_) | Constant::BigInt(_) => None,
470 Constant::Boolean(value) => Some(Value::boolean(*value)),
471 Constant::Null => Some(Value::NULL),
472 Constant::Undefined => Some(Value::UNDEFINED),
473 }
474}
475
476#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
480enum PropertyKey {
481 Named(EcmaString),
482 Symbol(u32),
483 Private(u32),
484}
485
486impl PropertyKey {
487 fn as_string(&self) -> Option<&EcmaString> {
488 match self {
489 PropertyKey::Named(text) => Some(text),
490 PropertyKey::Symbol(_) | PropertyKey::Private(_) => None,
491 }
492 }
493
494 fn eq_ascii(&self, ascii: &str) -> bool {
495 matches!(self, PropertyKey::Named(text) if text.eq_ascii(ascii))
496 }
497
498 fn charge_bytes(&self) -> usize {
499 match self {
500 PropertyKey::Named(text) => text.len_units().saturating_mul(2).saturating_add(8),
501 PropertyKey::Symbol(_) | PropertyKey::Private(_) => 16,
502 }
503 }
504}
505
506#[derive(Clone, Debug)]
508enum Property {
509 Data {
510 value: Value,
511 writable: bool,
512 enumerable: bool,
513 configurable: bool,
514 },
515 Accessor {
516 getter: Option<Value>,
517 setter: Option<Value>,
518 enumerable: bool,
519 configurable: bool,
520 },
521}
522impl Property {
523 fn enumerable(&self) -> bool {
524 match self {
525 Self::Data { enumerable, .. } | Self::Accessor { enumerable, .. } => *enumerable,
526 }
527 }
528
529 fn configurable(&self) -> bool {
530 match self {
531 Self::Data { configurable, .. } | Self::Accessor { configurable, .. } => *configurable,
532 }
533 }
534}
535
536#[derive(Clone, Debug, Default)]
539struct PropertyMap(Vec<(PropertyKey, Property)>);
540
541impl PropertyMap {
542 fn get(&self, key: &PropertyKey) -> Option<&Property> {
543 self.0
544 .iter()
545 .find_map(|(candidate, property)| (candidate == key).then_some(property))
546 }
547
548 fn get_mut(&mut self, key: &PropertyKey) -> Option<&mut Property> {
549 self.0
550 .iter_mut()
551 .find_map(|(candidate, property)| (candidate == key).then_some(property))
552 }
553
554 fn contains_key(&self, key: &PropertyKey) -> bool {
555 self.get(key).is_some()
556 }
557
558 fn get_ascii(&self, ascii: &str) -> Option<&Property> {
559 debug_assert!(ascii.is_ascii());
560 self.0
561 .iter()
562 .find_map(|(key, property)| key.eq_ascii(ascii).then_some(property))
563 }
564
565 fn insert(&mut self, key: PropertyKey, property: Property) -> Option<Property> {
566 if let Some(existing) = self.get_mut(&key) {
567 return Some(std::mem::replace(existing, property));
568 }
569 self.0.push((key, property));
570 None
571 }
572
573 fn remove(&mut self, key: &PropertyKey) -> Option<Property> {
574 let index = self.0.iter().position(|(candidate, _)| candidate == key)?;
575 Some(self.0.remove(index).1)
576 }
577
578 fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Property)> {
579 self.0.iter().map(|(key, property)| (key, property))
580 }
581 fn charge_bytes(&self) -> usize {
582 self.0.iter().fold(0, |bytes, (key, _)| {
583 bytes.saturating_add(key.charge_bytes())
584 })
585 }
586}
587
588impl<'a> IntoIterator for &'a PropertyMap {
589 type Item = (&'a PropertyKey, &'a Property);
590 type IntoIter = std::iter::Map<
591 std::slice::Iter<'a, (PropertyKey, Property)>,
592 fn(&(PropertyKey, Property)) -> (&PropertyKey, &Property),
593 >;
594
595 fn into_iter(self) -> Self::IntoIter {
596 fn pair_refs(pair: &(PropertyKey, Property)) -> (&PropertyKey, &Property) {
597 (&pair.0, &pair.1)
598 }
599 self.0.iter().map(pair_refs)
600 }
601}
602
603#[derive(Clone, Copy, Debug, Eq, PartialEq)]
604enum IterationKind {
605 Key,
606 Value,
607 Entry,
608}
609
610#[derive(Clone, Copy, Debug)]
611struct CollectionEntry {
612 order: u64,
613 key: Value,
614 value: Value,
615}
616
617impl CollectionEntry {
618 const BYTES: usize = std::mem::size_of::<Self>();
619}
620
621#[derive(Clone, Copy, Debug)]
622pub(crate) enum IteratorNextPrepared {
623 Ready { done: bool, value: Value },
624 Call { callee: Value, this_value: Value },
625}
626
627#[derive(Clone, Debug)]
628enum IteratorState {
629 Keys { index: usize, keys: Vec<EcmaString> },
630 Protocol { iterator: Value, next: Value },
631}
632
633#[derive(Clone, Debug)]
634pub(crate) struct GeneratorStart {
635 pub(crate) target: RuntimeFunction,
636 pub(crate) captures: Vec<Value>,
637 pub(crate) this_value: Value,
638 pub(crate) new_target: Value,
639 pub(crate) args: Vec<Value>,
640}
641
642#[derive(Clone, Debug)]
643pub(crate) struct SuspendedActivation {
644 pub(crate) target: RuntimeFunction,
645 pub(crate) registers: Vec<Value>,
646 pub(crate) this_value: Value,
647 pub(crate) new_target: Value,
648 pub(crate) args: Vec<Value>,
649 pub(crate) arguments_object: Option<Value>,
650 pub(crate) resume_token: u32,
651}
652
653#[derive(Clone, Debug)]
654pub(crate) enum GeneratorState {
655 SuspendedStart(GeneratorStart),
656 Executing,
657 Suspended(SuspendedActivation),
658 Completed,
659}
660
661#[derive(Clone, Debug)]
662pub(crate) enum GeneratorResume {
663 Yield {
664 value: Value,
665 activation: SuspendedActivation,
666 },
667 Return(Value),
668 Throw {
669 value: Value,
670 origin: ThrowOrigin,
671 },
672}
673
674#[derive(Clone, Debug)]
677enum AsyncStep {
678 Suspend {
679 awaited: Value,
680 activation: SuspendedActivation,
681 },
682 Return(Value),
683 Throw {
684 value: Value,
685 origin: ThrowOrigin,
686 },
687}
688
689#[derive(Clone, Debug)]
690pub(crate) enum PromiseState {
691 Pending {
692 fulfill_reactions: Vec<PromiseReaction>,
693 reject_reactions: Vec<PromiseReaction>,
694 },
695 Fulfilled {
696 value: Value,
697 },
698 Rejected {
699 reason: Value,
700 origin: ThrowOrigin,
701 },
702}
703
704#[derive(Clone, Copy, Debug)]
705pub(crate) enum PromiseCompletion {
706 Fulfilled,
707 Rejected,
708}
709
710#[derive(Clone, Debug)]
711pub(crate) enum PromiseReaction {
712 Fulfilled {
713 handler: Value,
714 derived: Value,
715 },
716 Rejected {
717 handler: Value,
718 derived: Value,
719 },
720 Finally {
721 handler: Value,
722 derived: Value,
723 completion: PromiseCompletion,
724 },
725 AsyncFulfill {
729 activation: Value,
730 },
731 AsyncReject {
735 activation: Value,
736 },
737}
738
739#[derive(Clone, Debug)]
740pub(crate) enum MicrotaskJob {
741 Reaction {
742 reaction: PromiseReaction,
743 value: Value,
744 origin: ThrowOrigin,
745 },
746 Thenable {
747 promise: Value,
748 thenable: Value,
749 then: Value,
750 },
751 Callback {
752 callback: Value,
753 },
754}
755
756#[derive(Clone, Debug, Eq, PartialEq)]
758pub struct CallbackException {
759 pub value: Value,
761 pub origin: ThrowOrigin,
763}
764
765#[derive(Clone, Debug, Default, Eq, PartialEq)]
767pub struct MicrotaskDrain {
768 pub executed: usize,
770 pub uncaught: Vec<CallbackException>,
772}
773
774#[derive(Clone, Debug, Default, Eq, PartialEq)]
776pub struct TimerRun {
777 pub executed: usize,
779 pub uncaught: Vec<CallbackException>,
781}
782
783#[derive(Clone, Debug)]
784enum HeapEntry {
785 String(EcmaString),
786 BigInt(String),
787 Object {
788 properties: PropertyMap,
789 prototype: Option<Value>,
790 boxed_primitive: Option<Value>,
791 extensible: bool,
792 },
793 Array {
794 elements: Vec<Value>,
795 properties: PropertyMap,
796 prototype: Option<Value>,
797 extensible: bool,
798 length_writable: bool,
799 },
800 Function {
801 module: ModuleId,
802 function: FunctionId,
803 captures: Vec<Value>,
804 properties: PropertyMap,
805 prototype: Option<Value>,
806 extensible: bool,
807 },
808 Script {
810 entry: Value,
811 properties: PropertyMap,
812 prototype: Option<Value>,
813 extensible: bool,
814 },
815 ModuleNamespace {
816 module: ModuleId,
817 },
818 ExternalModuleNamespace {
819 specifier: EcmaString,
820 },
821 HashState {
822 algorithm: String,
823 data: Vec<u8>,
824 digested: bool,
825 update: Value,
826 digest: Value,
827 },
828 Symbol {
829 description: EcmaString,
830 },
831 PrivateName {
832 description: EcmaString,
833 },
834 RegExp {
835 pattern: EcmaString,
836 flags: EcmaString,
837 properties: PropertyMap,
838 prototype: Option<Value>,
839 extensible: bool,
840 },
841 Date {
842 time: f64,
843 properties: PropertyMap,
844 prototype: Option<Value>,
845 extensible: bool,
846 },
847 Collection {
851 entries: Vec<CollectionEntry>,
852 next_order: u64,
853 properties: PropertyMap,
854 prototype: Option<Value>,
855 extensible: bool,
856 },
857 BuiltinIterator {
858 source: Value,
859 kind: IterationKind,
860 position: Option<u64>,
861 properties: PropertyMap,
862 prototype: Option<Value>,
863 extensible: bool,
864 },
865 Iterator {
866 state: IteratorState,
867 },
868 Generator {
869 state: GeneratorState,
870 properties: PropertyMap,
871 prototype: Option<Value>,
872 extensible: bool,
873 },
874 ProcessEnv {
875 prototype: Option<Value>,
876 extensible: bool,
877 },
878 Promise {
879 state: PromiseState,
880 properties: PropertyMap,
881 prototype: Option<Value>,
882 extensible: bool,
883 },
884 Timeout {
887 id: u64,
888 properties: PropertyMap,
889 prototype: Option<Value>,
890 extensible: bool,
891 },
892 PromiseResolver {
893 promise: Value,
894 used: bool,
895 },
896 PromiseFinally {
897 derived: Value,
898 value: Value,
899 origin: ThrowOrigin,
900 completion: PromiseCompletion,
901 },
902 PromiseAll {
903 promise: Value,
904 values: Vec<Value>,
905 remaining: usize,
906 settled: bool,
907 },
908 PromiseAllElement {
909 aggregate: Value,
910 index: usize,
911 called: bool,
912 },
913 AsyncActivation {
918 activation: Option<SuspendedActivation>,
919 promise: Value,
920 },
921 NativeFunction {
922 callable: NativeCallable,
923 properties: PropertyMap,
924 extensible: bool,
925 },
926}
927
928#[derive(Clone, Debug)]
929pub(crate) enum NativeCallable {
930 Builtin(intrinsics::BuiltinId),
931 Bound(Box<BoundCallable>),
932}
933
934#[derive(Clone, Debug)]
935pub(crate) struct BoundCallable {
936 pub(crate) target: Value,
937 pub(crate) this_value: Value,
938 pub(crate) arguments: Vec<Value>,
939}
940
941impl HeapEntry {
942 fn initial_bytes(&self) -> usize {
943 match self {
944 Self::String(text)
945 | Self::Symbol { description: text }
946 | Self::PrivateName { description: text } => text.len_units().saturating_mul(2),
947 Self::BigInt(text) => text.len(),
948 Self::RegExp { pattern, flags, .. } => pattern
949 .len_units()
950 .saturating_add(flags.len_units())
951 .saturating_mul(2),
952 Self::HashState {
953 algorithm, data, ..
954 } => algorithm.len() + data.len(),
955 Self::Collection { entries, .. } => entries
956 .len()
957 .saturating_mul(CollectionEntry::BYTES)
958 .saturating_add(1),
959 Self::NativeFunction { callable, .. } => match callable {
960 NativeCallable::Builtin(_) => 1,
961 NativeCallable::Bound(bound) => bound.arguments.len().saturating_add(1),
962 },
963 Self::Generator { state, .. } => match state {
964 GeneratorState::SuspendedStart(start) => start
965 .captures
966 .len()
967 .saturating_add(start.args.len())
968 .saturating_add(1),
969 GeneratorState::Suspended(activation) => activation
970 .registers
971 .len()
972 .saturating_add(activation.args.len())
973 .saturating_add(1),
974 GeneratorState::Executing | GeneratorState::Completed => 1,
975 },
976 Self::Object { properties, .. } | Self::Timeout { properties, .. } => {
977 properties.charge_bytes().saturating_add(1)
978 }
979 Self::Array { .. }
980 | Self::Function { .. }
981 | Self::Script { .. }
982 | Self::ModuleNamespace { .. }
983 | Self::ExternalModuleNamespace { .. }
984 | Self::ProcessEnv { .. }
985 | Self::Date { .. }
986 | Self::BuiltinIterator { .. }
987 | Self::Iterator { .. }
988 | Self::Promise { .. }
989 | Self::PromiseResolver { .. }
990 | Self::PromiseFinally { .. }
991 | Self::PromiseAll { .. }
992 | Self::AsyncActivation { .. }
993 | Self::PromiseAllElement { .. } => 1,
994 }
995 }
996}
997
998#[derive(Clone, Copy, Debug)]
999struct ReturnTo {
1000 destination: Option<usize>,
1003 call_pc: usize,
1004 constructed: Option<Value>,
1005}
1006
1007struct CallRequest<'a> {
1008 callee: Value,
1009 this_value: Value,
1010 arguments: &'a [Value],
1011 destination: Option<u32>,
1012 call_pc: usize,
1013 constructed: Option<Value>,
1014 new_target: Value,
1015}
1016
1017pub(crate) struct BoundCall {
1018 pub(crate) target: Value,
1019 pub(crate) this_value: Value,
1020 pub(crate) arguments: Vec<Value>,
1021}
1022
1023#[derive(Clone, Copy, Debug)]
1024pub(crate) struct RuntimeFunction {
1025 pub(crate) module: ModuleId,
1026 pub(crate) function: FunctionId,
1027}
1028
1029#[derive(Clone, Debug)]
1030struct Frame {
1031 module: ModuleId,
1032 function: usize,
1033 pc: usize,
1034 registers: Vec<Value>,
1035 return_to: Option<ReturnTo>,
1036 this_value: Value,
1037 new_target: Value,
1038 args: Vec<Value>,
1039 arguments_object: Option<Value>,
1040}
1041
1042impl Frame {
1043 fn new(
1044 target: RuntimeFunction,
1045 metadata: &Function,
1046 captures: &[Value],
1047 this_value: Value,
1048 new_target: Value,
1049 arguments: &[Value],
1050 return_to: Option<ReturnTo>,
1051 ) -> Self {
1052 let mut registers = vec![Value::UNINITIALIZED; metadata.register_count() as usize];
1053 let capture_count = metadata.capture_count() as usize;
1054 for (index, slot) in registers.iter_mut().take(capture_count).enumerate() {
1055 *slot = captures.get(index).copied().unwrap_or(Value::UNDEFINED);
1056 }
1057 for (index, slot) in registers
1058 .iter_mut()
1059 .skip(capture_count)
1060 .take(metadata.parameter_count() as usize)
1061 .enumerate()
1062 {
1063 *slot = arguments.get(index).copied().unwrap_or(Value::UNDEFINED);
1064 }
1065 Self {
1066 module: target.module,
1067 function: target.function.get() as usize,
1068 pc: 0,
1069 registers,
1070 return_to,
1071 this_value,
1072 new_target,
1073 args: arguments.to_vec(),
1074 arguments_object: None,
1075 }
1076 }
1077}
1078
1079#[derive(Clone, Debug)]
1080pub(crate) enum EvalFailure {
1081 Throw(ThrowOrigin),
1082 ThrowValue(Value),
1083 Runtime(RuntimeErrorKind),
1084 ThrowValueOrigin { value: Value, origin: ThrowOrigin },
1085}
1086
1087pub(crate) fn import_failure(error: &RuntimeError) -> EvalFailure {
1088 match &error.kind {
1089 RuntimeErrorKind::UncaughtThrow { value, .. } => EvalFailure::ThrowValue(*value),
1090 kind => EvalFailure::Runtime(kind.clone()),
1091 }
1092}
1093
1094#[derive(Clone, Debug)]
1096enum GetOutcome {
1097 Value(Value),
1099 Text(EcmaString),
1101 Getter(Value),
1103}
1104
1105#[derive(Clone, Debug)]
1107enum SetOutcome {
1108 Done,
1109 Setter(Value),
1112}
1113
1114#[derive(Clone, Debug)]
1116enum Found {
1117 Value(Value),
1118 Text(EcmaString),
1119 Getter(Value),
1120 Failure(RuntimeErrorKind),
1121 NoGetter,
1123}
1124
1125#[derive(Clone, Debug)]
1129enum CalleeKind {
1130 Runtime {
1131 target: RuntimeFunction,
1132 captures: Vec<Value>,
1133 },
1134 Builtin {
1135 id: intrinsics::BuiltinId,
1136 },
1137 Bound,
1138 NotCallable,
1139}
1140
1141#[derive(Clone, Debug)]
1142struct TimerRecord {
1143 callback: Value,
1144 arguments: Vec<Value>,
1145 handle: Value,
1146 deadline_ms: u64,
1147 sequence: u64,
1148}
1149
1150pub struct Machine<'a, H: Host> {
1152 program: Option<&'a Program<Verified>>,
1153 module: &'a Module<Verified>,
1154 host: &'a mut H,
1155 limits: Limits,
1156 frames: Vec<Frame>,
1157 heap: Vec<HeapEntry>,
1158 intrinsic_slots: usize,
1159 heap_bytes: usize,
1160 live_registers: usize,
1161 native_depth: usize,
1162 fuel: u64,
1163 globals: BTreeMap<EcmaString, Value>,
1164 last_completion: Option<Value>,
1165 callback_boundaries: Vec<usize>,
1169 generator_boundaries: Vec<usize>,
1170 pending_generator_resume: Option<GeneratorResume>,
1171 async_boundaries: Vec<usize>,
1172 pending_async_suspend: Option<(Value, SuspendedActivation)>,
1173 microtasks: VecDeque<MicrotaskJob>,
1174 microtask_drain_active: bool,
1175 next_timer_id: Option<u64>,
1176 next_timer_sequence: Option<u64>,
1177 timers: BTreeMap<u64, TimerRecord>,
1178 ready_timers: BTreeSet<(u64, u64)>,
1179 timer_watermark: Option<u64>,
1180 timer_checkpoint_active: bool,
1181 intrinsics: intrinsics::Intrinsics<H>,
1182 current_builtin_id: Option<intrinsics::BuiltinId>,
1183 registry: ModuleRegistry,
1184 dynamic_base: usize,
1186 dynamic: Vec<DynamicModule>,
1189}
1190
1191#[derive(Clone, Debug)]
1192struct DynamicModule {
1193 program: Arc<Program<Verified>>,
1194 bytes: usize,
1195}
1196
1197#[derive(Clone, Debug, Default)]
1198struct ModuleRegistry {
1199 modules: Vec<ModuleInstance>,
1200 cells: Vec<Cell>,
1201 external: BTreeMap<EcmaString, ExternalModuleInstance>,
1202}
1203
1204#[derive(Clone, Debug)]
1205struct ExternalModuleInstance {
1206 namespace: Value,
1207 exports: BTreeMap<EcmaString, ExternalExport>,
1208 internals: BTreeMap<&'static str, Value>,
1209}
1210
1211#[derive(Clone, Copy, Debug)]
1212struct ExternalExport {
1213 value: Value,
1214 cell: Option<CellId>,
1215}
1216
1217#[derive(Clone, Debug)]
1218struct ModuleInstance {
1219 binding_cells: Vec<Option<CellId>>,
1220 constant_cells: Vec<Option<CellId>>,
1221 namespace: Option<Value>,
1222 state: ModuleState,
1223}
1224
1225#[derive(Clone, Debug)]
1226enum ModuleState {
1227 Unevaluated,
1228 Evaluating,
1229 Evaluated(Result<(), RuntimeError>),
1230}
1231
1232#[derive(Clone, Debug)]
1233pub(crate) enum ModuleEvaluation {
1234 Cycle,
1235 Evaluated(Result<(), RuntimeError>),
1236 Ready(Vec<ModuleId>),
1237}
1238
1239#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1240pub(crate) enum ImportTarget {
1241 Local(ModuleId),
1242 External(EdgeId),
1243}
1244
1245#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1246struct CellId(usize);
1247
1248#[derive(Clone, Copy, Debug)]
1249struct Cell {
1250 value: Value,
1251}
1252
1253pub fn run<H: Host>(
1254 program: &Program<Verified>,
1255 host: &mut H,
1256 limits: &Limits,
1257) -> Result<ExecutionOutcome, RuntimeError> {
1258 Machine::new(program, host, limits.clone())
1259 .run()
1260 .map(|execution| execution.outcome)
1261}
1262
1263impl<'a, H: Host> Machine<'a, H> {
1264 #[must_use]
1265 pub fn new(program: &'a Program<Verified>, host: &'a mut H, limits: Limits) -> Self {
1266 let module = &program
1267 .module(program.entry())
1268 .expect("verified program entry exists")
1269 .code;
1270 Self::build(Some(program), module, host, limits)
1271 }
1272
1273 fn build(
1274 program: Option<&'a Program<Verified>>,
1275 module: &'a Module<Verified>,
1276 host: &'a mut H,
1277 limits: Limits,
1278 ) -> Self {
1279 let entry = module.entry().get() as usize;
1280 let module_id = program.map_or(ModuleId::new(0), Program::entry);
1281 let frame = Frame::new(
1282 RuntimeFunction {
1283 module: module_id,
1284 function: FunctionId::new(entry as u32),
1285 },
1286 &module.functions()[entry],
1287 &[],
1288 Value::UNDEFINED,
1289 Value::UNDEFINED,
1290 &[],
1291 None,
1292 );
1293 let live_registers = frame.registers.len();
1294 let mut heap = Vec::new();
1295 let timers_available = host.timers().is_some();
1296 let mut intrinsics = intrinsics::Intrinsics::<H>::initialize(&mut heap, timers_available);
1297 let script_compiler = host.script_compiler().is_some();
1298 let installed_external = external_modules::install(
1299 &mut heap,
1300 &mut intrinsics.builtins,
1301 intrinsics.object_prototype,
1302 script_compiler,
1303 );
1304 let argv_text = host.argv().to_vec();
1305 let argv_values: Vec<Value> = argv_text
1306 .into_iter()
1307 .map(|text| {
1308 intrinsics::push(&mut heap, HeapEntry::String(EcmaString::from_utf8(&text)))
1309 })
1310 .collect();
1311 let process = intrinsics
1312 .global("process")
1313 .expect("host objects install process");
1314 let Some(Decoded::HeapRef(process_id)) = process.decode() else {
1315 unreachable!("process is an engine object");
1316 };
1317 let process_index = process_id.slot() as usize - 1;
1318 let HeapEntry::Object { properties, .. } = &heap[process_index] else {
1319 unreachable!("process is an ordinary object");
1320 };
1321 let Some(Property::Data { value: argv, .. }) = properties.get_ascii("argv") else {
1322 unreachable!("process owns argv");
1323 };
1324 let Some(Decoded::HeapRef(argv_id)) = argv.decode() else {
1325 unreachable!("process.argv is an engine array");
1326 };
1327 let argv_index = argv_id.slot() as usize - 1;
1328 let HeapEntry::Array { elements, .. } = &mut heap[argv_index] else {
1329 unreachable!("process.argv is an array");
1330 };
1331 *elements = argv_values;
1332 let intrinsic_slots = heap.len();
1333 let fuel = limits.fuel;
1334 Self {
1335 program,
1336 module,
1337 host,
1338 limits,
1339 fuel,
1340 frames: vec![frame],
1341 heap,
1342 heap_bytes: 0,
1343 intrinsic_slots,
1344 live_registers,
1345 native_depth: 0,
1346 last_completion: None,
1347 callback_boundaries: Vec::new(),
1348 generator_boundaries: Vec::new(),
1349 pending_generator_resume: None,
1350 async_boundaries: Vec::new(),
1351 pending_async_suspend: None,
1352 microtasks: VecDeque::new(),
1353 microtask_drain_active: false,
1354 next_timer_id: Some(1),
1355 next_timer_sequence: Some(0),
1356 timers: BTreeMap::new(),
1357 ready_timers: BTreeSet::new(),
1358 timer_watermark: None,
1359 timer_checkpoint_active: false,
1360 globals: BTreeMap::new(),
1361 registry: ModuleRegistry {
1362 external: installed_external
1363 .into_iter()
1364 .map(|module| {
1365 let mut exports: BTreeMap<_, _> = module
1366 .exports
1367 .into_iter()
1368 .map(|(name, value)| (name, ExternalExport { value, cell: None }))
1369 .collect();
1370 exports.insert(
1371 EcmaString::from_utf8("default"),
1372 ExternalExport {
1373 value: module.namespace,
1374 cell: None,
1375 },
1376 );
1377 (
1378 module.specifier,
1379 ExternalModuleInstance {
1380 namespace: module.namespace,
1381 exports,
1382 internals: module.internals,
1383 },
1384 )
1385 })
1386 .collect(),
1387 ..ModuleRegistry::default()
1388 },
1389 dynamic_base: program.map_or(1, |program| program.modules().len()),
1390 dynamic: Vec::new(),
1391 current_builtin_id: None,
1392 intrinsics,
1393 }
1394 }
1395
1396 pub fn run(mut self) -> Result<Execution, RuntimeError> {
1397 self.evaluate()
1398 }
1399
1400 pub fn evaluate(&mut self) -> Result<Execution, RuntimeError> {
1405 if let Some(program) = self.program {
1406 let entry = program.entry();
1407 self.frames.clear();
1408 self.live_registers = 0;
1409 self.instantiate_modules()?;
1410 return self.evaluate_module(entry)?.ok_or_else(|| {
1411 self.program_error(
1412 entry,
1413 RuntimeErrorKind::InvalidVerifiedProgram {
1414 module: entry,
1415 instruction: Instruction::Halt,
1416 },
1417 )
1418 });
1419 }
1420 Ok(self
1421 .run_loop(0)?
1422 .expect("the entry frame completes before the run loop stops"))
1423 }
1424
1425 pub fn run_one_expired_timer(&mut self) -> Result<TimerRun, RuntimeError> {
1431 if self.timer_checkpoint_active {
1432 return Err(self.checkpoint_error(RuntimeErrorKind::TimerCheckpointReentry));
1433 }
1434 self.timer_checkpoint_active = true;
1435 let result = (|| {
1436 self.poll_timer_expiries()
1437 .map_err(|kind| self.checkpoint_error(kind))?;
1438 let Some(order) = self.ready_timers.first().copied() else {
1439 return Ok(TimerRun::default());
1440 };
1441 let Some(id) = self.timers.iter().find_map(|(id, timer)| {
1442 ((timer.deadline_ms, timer.sequence) == order).then_some(*id)
1443 }) else {
1444 return Err(self.checkpoint_error(RuntimeErrorKind::InvalidValue {
1445 value: Value::UNDEFINED,
1446 }));
1447 };
1448 self.consume_fuel(1)
1450 .map_err(|kind| self.checkpoint_error(kind))?;
1451 self.ready_timers.remove(&order);
1452 let timer = self
1453 .timers
1454 .remove(&id)
1455 .expect("ready timer remains live until after fuel charging");
1456 let mut report = TimerRun {
1457 executed: 1,
1458 uncaught: Vec::new(),
1459 };
1460 match self.call_value(timer.callback, timer.handle, &timer.arguments) {
1461 Ok(_) => {}
1462 Err(EvalFailure::Runtime(kind)) => {
1463 return Err(self.checkpoint_error(kind));
1464 }
1465 Err(failure) => {
1466 let (value, origin) =
1467 self.promise_rejection_value(failure)
1468 .map_err(|failure| match failure {
1469 EvalFailure::Runtime(kind) => self.checkpoint_error(kind),
1470 _ => self.checkpoint_error(RuntimeErrorKind::InvalidValue {
1471 value: timer.callback,
1472 }),
1473 })?;
1474 report.uncaught.try_reserve(1).map_err(|_| {
1475 self.checkpoint_error(RuntimeErrorKind::HeapByteLimitExceeded {
1476 limit: self.limits.max_heap_bytes,
1477 })
1478 })?;
1479 report.uncaught.push(CallbackException { value, origin });
1480 }
1481 }
1482 Ok(report)
1483 })();
1484 self.timer_checkpoint_active = false;
1485 result
1486 }
1487
1488 pub fn wait_for_timer_expiry(&mut self) -> Result<bool, RuntimeError> {
1491 if self.timer_checkpoint_active {
1492 return Err(self.checkpoint_error(RuntimeErrorKind::TimerCheckpointReentry));
1493 }
1494 if !self.ready_timers.is_empty() {
1495 return Ok(true);
1496 }
1497 self.timer_checkpoint_active = true;
1498 let result = (|| {
1499 let wakeup = match self.host.timers() {
1500 Some(provider) => provider.wait_expired(),
1501 None => return Ok(false),
1502 }
1503 .map_err(|error| {
1504 self.checkpoint_error(RuntimeErrorKind::TimerProviderFailure {
1505 message: error.to_string(),
1506 })
1507 })?;
1508 if let Some(wakeup) = wakeup {
1509 self.promote_timer_wakeup(wakeup);
1510 }
1511 Ok(!self.ready_timers.is_empty())
1512 })();
1513 self.timer_checkpoint_active = false;
1514 result
1515 }
1516
1517 #[must_use]
1519 pub fn has_pending_timers(&self) -> bool {
1520 !self.timers.is_empty()
1521 }
1522
1523 pub(crate) fn schedule_timeout(
1524 &mut self,
1525 callback: Value,
1526 delay_ms: u32,
1527 arguments: Vec<Value>,
1528 ) -> Result<Value, EvalFailure> {
1529 if self.timers.len() >= self.limits.max_timers {
1530 return Err(EvalFailure::Runtime(
1531 RuntimeErrorKind::TimerCapacityExceeded {
1532 limit: self.limits.max_timers,
1533 },
1534 ));
1535 }
1536 let id = self.next_timer_id.take().ok_or(EvalFailure::Runtime(
1537 RuntimeErrorKind::TimerCapacityExceeded {
1538 limit: self.limits.max_timers,
1539 },
1540 ))?;
1541 self.next_timer_id = id.checked_add(1);
1542 let sequence = self.next_timer_sequence.take().ok_or(EvalFailure::Runtime(
1543 RuntimeErrorKind::TimerCapacityExceeded {
1544 limit: self.limits.max_timers,
1545 },
1546 ))?;
1547 self.next_timer_sequence = sequence.checked_add(1);
1548 let deadline_ms = self
1549 .host
1550 .timers()
1551 .ok_or(EvalFailure::Runtime(
1552 RuntimeErrorKind::TimerProviderFailure {
1553 message: "timer capability is unavailable".to_owned(),
1554 },
1555 ))?
1556 .schedule(id, delay_ms)
1557 .map_err(|error| {
1558 EvalFailure::Runtime(RuntimeErrorKind::TimerProviderFailure {
1559 message: error.to_string(),
1560 })
1561 })?;
1562 let handle = match self.allocate(HeapEntry::Timeout {
1563 id,
1564 properties: PropertyMap::default(),
1565 prototype: Some(self.intrinsics.object_prototype),
1566 extensible: true,
1567 }) {
1568 Ok(handle) => handle,
1569 Err(kind) => {
1570 if let Some(provider) = self.host.timers() {
1571 let _ = provider.cancel(id);
1572 }
1573 return Err(EvalFailure::Runtime(kind));
1574 }
1575 };
1576 self.timers.insert(
1577 id,
1578 TimerRecord {
1579 callback,
1580 arguments,
1581 handle,
1582 deadline_ms,
1583 sequence,
1584 },
1585 );
1586 Ok(handle)
1587 }
1588
1589 pub(crate) fn clear_timeout(&mut self, handle: Value) -> Result<(), EvalFailure> {
1590 let id = match handle.decode() {
1591 Some(Decoded::Int32(raw)) if (raw as i32) > 0 => Some(u64::from(raw)),
1592 Some(Decoded::Number(number))
1593 if number.is_finite()
1594 && number > 0.0
1595 && number.fract() == 0.0
1596 && number < u64::MAX as f64 =>
1597 {
1598 Some(number as u64)
1599 }
1600 Some(Decoded::HeapRef(_)) => {
1601 self.runtime_slot(handle)
1602 .ok()
1603 .flatten()
1604 .and_then(|index| match &self.heap[index] {
1605 HeapEntry::Timeout { id, .. } => Some(*id),
1606 _ => None,
1607 })
1608 }
1609 _ => None,
1610 };
1611 let Some(id) = id else {
1612 return Ok(());
1613 };
1614 let Some(timer) = self.timers.remove(&id) else {
1615 return Ok(());
1616 };
1617 self.ready_timers
1618 .remove(&(timer.deadline_ms, timer.sequence));
1619 if let Some(provider) = self.host.timers() {
1620 provider.cancel(id).map_err(|error| {
1621 EvalFailure::Runtime(RuntimeErrorKind::TimerProviderFailure {
1622 message: error.to_string(),
1623 })
1624 })?;
1625 }
1626 Ok(())
1627 }
1628
1629 fn poll_timer_expiries(&mut self) -> Result<(), RuntimeErrorKind> {
1630 let mut wakeups = Vec::new();
1631 let Some(provider) = self.host.timers() else {
1632 return Ok(());
1633 };
1634 provider.poll_expired(&mut wakeups).map_err(|error| {
1635 RuntimeErrorKind::TimerProviderFailure {
1636 message: error.to_string(),
1637 }
1638 })?;
1639 if let Some(wakeup) = wakeups
1642 .into_iter()
1643 .filter(|wakeup| self.timers.contains_key(&wakeup.id))
1644 .max_by_key(|wakeup| wakeup.deadline_ms)
1645 {
1646 self.promote_timer_wakeup(wakeup);
1647 }
1648 Ok(())
1649 }
1650
1651 fn promote_timer_wakeup(&mut self, wakeup: TimerWakeup) {
1652 if !self.timers.contains_key(&wakeup.id) {
1655 return;
1656 }
1657 let watermark = self.timer_watermark.map_or(wakeup.deadline_ms, |current| {
1658 current.max(wakeup.deadline_ms)
1659 });
1660 self.timer_watermark = Some(watermark);
1661 for timer in self.timers.values() {
1662 if timer.deadline_ms <= watermark {
1663 self.ready_timers
1664 .insert((timer.deadline_ms, timer.sequence));
1665 }
1666 }
1667 }
1668
1669 pub fn run_to_quiescence(&mut self) -> Result<(), RuntimeError> {
1671 loop {
1672 let microtasks = self.drain_microtasks()?;
1673 let timer = self.run_one_expired_timer()?;
1674 if microtasks.executed == 0 && timer.executed == 0 {
1675 break;
1676 }
1677 }
1678 Ok(())
1679 }
1680
1681 pub fn drain_microtasks(&mut self) -> Result<MicrotaskDrain, RuntimeError> {
1687 if self.microtask_drain_active {
1688 return Err(self.checkpoint_error(RuntimeErrorKind::MicrotaskDrainReentry));
1689 }
1690 self.microtask_drain_active = true;
1691 let result = (|| {
1692 let mut report = MicrotaskDrain::default();
1693 while self.microtasks.front().is_some() {
1694 self.consume_fuel(1)
1695 .map_err(|kind| self.checkpoint_error(kind))?;
1696 let job = self
1697 .microtasks
1698 .pop_front()
1699 .expect("the queued microtask remains present after fuel charging");
1700 report.executed = report.executed.saturating_add(1);
1701 self.execute_microtask_job(job, &mut report)
1702 .map_err(|kind| self.checkpoint_error(kind))?;
1703 }
1704 Ok(report)
1705 })();
1706 self.microtask_drain_active = false;
1707 result
1708 }
1709
1710 fn checkpoint_error(&self, kind: RuntimeErrorKind) -> RuntimeError {
1711 let function = self.module.entry();
1712 let instruction = self.module.functions()[function.get() as usize]
1713 .code()
1714 .first()
1715 .copied()
1716 .unwrap_or(Instruction::Halt);
1717 RuntimeError {
1718 kind,
1719 function,
1720 pc: Pc::new(0),
1721 source: RuntimeSource {
1722 function_name: None,
1723 instruction,
1724 },
1725 }
1726 }
1727
1728 fn execute_microtask_job(
1729 &mut self,
1730 job: MicrotaskJob,
1731 report: &mut MicrotaskDrain,
1732 ) -> Result<(), RuntimeErrorKind> {
1733 match job {
1734 MicrotaskJob::Reaction {
1735 reaction,
1736 value,
1737 origin,
1738 } => self.execute_promise_reaction(reaction, value, origin),
1739 MicrotaskJob::Thenable {
1740 promise,
1741 thenable,
1742 then,
1743 } => self.execute_thenable_job(promise, thenable, then),
1744 MicrotaskJob::Callback { callback } => {
1745 self.execute_callback_microtask(callback, report)
1746 }
1747 }
1748 }
1749
1750 fn execute_callback_microtask(
1751 &mut self,
1752 callback: Value,
1753 report: &mut MicrotaskDrain,
1754 ) -> Result<(), RuntimeErrorKind> {
1755 match self.call_value(callback, Value::UNDEFINED, &[]) {
1756 Ok(_) => Ok(()),
1757 Err(EvalFailure::Runtime(kind)) => Err(kind),
1758 Err(failure) => {
1759 let (value, origin) =
1760 self.promise_rejection_value(failure)
1761 .map_err(|failure| match failure {
1762 EvalFailure::Runtime(kind) => kind,
1763 _ => RuntimeErrorKind::InvalidValue { value: callback },
1764 })?;
1765 report.uncaught.try_reserve(1).map_err(|_| {
1766 RuntimeErrorKind::HeapByteLimitExceeded {
1767 limit: self.limits.max_heap_bytes,
1768 }
1769 })?;
1770 report.uncaught.push(CallbackException { value, origin });
1771 Ok(())
1772 }
1773 }
1774 }
1775
1776 fn execute_thenable_job(
1777 &mut self,
1778 promise: Value,
1779 thenable: Value,
1780 then: Value,
1781 ) -> Result<(), RuntimeErrorKind> {
1782 let record = self
1783 .create_promise_resolver(promise)
1784 .map_err(|failure| match failure {
1785 EvalFailure::Runtime(kind) => kind,
1786 _ => RuntimeErrorKind::InvalidValue { value: promise },
1787 })?;
1788 let (resolve_target, reject_target) = self.intrinsics.builtins.promise_resolver_targets();
1789 let resolve = self
1790 .create_promise_resolver_function(resolve_target, record)
1791 .map_err(|failure| match failure {
1792 EvalFailure::Runtime(kind) => kind,
1793 _ => RuntimeErrorKind::InvalidValue { value: record },
1794 })?;
1795 let reject = self
1796 .create_promise_resolver_function(reject_target, record)
1797 .map_err(|failure| match failure {
1798 EvalFailure::Runtime(kind) => kind,
1799 _ => RuntimeErrorKind::InvalidValue { value: record },
1800 })?;
1801 match self.call_value(then, thenable, &[resolve, reject]) {
1802 Ok(_) => Ok(()),
1803 Err(EvalFailure::Runtime(kind)) => Err(kind),
1804 Err(failure) => self
1805 .reject_promise_resolver_failure(record, failure)
1806 .map_err(|failure| match failure {
1807 EvalFailure::Runtime(kind) => kind,
1808 _ => RuntimeErrorKind::InvalidValue { value: record },
1809 }),
1810 }
1811 }
1812
1813 fn execute_promise_reaction(
1814 &mut self,
1815 reaction: PromiseReaction,
1816 value: Value,
1817 origin: ThrowOrigin,
1818 ) -> Result<(), RuntimeErrorKind> {
1819 match reaction {
1820 PromiseReaction::Fulfilled { handler, derived } => self.execute_promise_handler(
1821 handler,
1822 derived,
1823 value,
1824 origin,
1825 PromiseCompletion::Fulfilled,
1826 ),
1827 PromiseReaction::Rejected { handler, derived } => self.execute_promise_handler(
1828 handler,
1829 derived,
1830 value,
1831 origin,
1832 PromiseCompletion::Rejected,
1833 ),
1834 PromiseReaction::Finally {
1835 handler,
1836 derived,
1837 completion,
1838 } => self.execute_promise_finally(handler, derived, value, origin, completion),
1839 PromiseReaction::AsyncFulfill { activation } => {
1840 self.resume_async(activation, value, None)
1841 }
1842 PromiseReaction::AsyncReject { activation } => {
1843 self.resume_async(activation, value, Some(origin))
1844 }
1845 }
1846 }
1847
1848 fn execute_promise_handler(
1849 &mut self,
1850 handler: Value,
1851 derived: Value,
1852 value: Value,
1853 origin: ThrowOrigin,
1854 completion: PromiseCompletion,
1855 ) -> Result<(), RuntimeErrorKind> {
1856 if !self.is_callable(handler).map_err(|failure| match failure {
1857 EvalFailure::Runtime(kind) => kind,
1858 _ => RuntimeErrorKind::InvalidValue { value: handler },
1859 })? {
1860 return match completion {
1861 PromiseCompletion::Fulfilled => self.resolve_promise(derived, value),
1862 PromiseCompletion::Rejected => self.reject_promise(derived, value, origin),
1863 };
1864 }
1865 match self.call_value(handler, Value::UNDEFINED, &[value]) {
1866 Ok(result) => self.resolve_promise(derived, result),
1867 Err(EvalFailure::Runtime(kind)) => Err(kind),
1868 Err(failure) => self
1869 .reject_promise_failure(derived, failure)
1870 .map_err(|failure| match failure {
1871 EvalFailure::Runtime(kind) => kind,
1872 _ => RuntimeErrorKind::InvalidValue { value: derived },
1873 }),
1874 }
1875 }
1876
1877 fn execute_promise_finally(
1878 &mut self,
1879 handler: Value,
1880 derived: Value,
1881 value: Value,
1882 origin: ThrowOrigin,
1883 completion: PromiseCompletion,
1884 ) -> Result<(), RuntimeErrorKind> {
1885 if !self.is_callable(handler).map_err(|failure| match failure {
1886 EvalFailure::Runtime(kind) => kind,
1887 _ => RuntimeErrorKind::InvalidValue { value: handler },
1888 })? {
1889 return match completion {
1890 PromiseCompletion::Fulfilled => self.resolve_promise(derived, value),
1891 PromiseCompletion::Rejected => self.reject_promise(derived, value, origin),
1892 };
1893 }
1894 let cleanup = self.create_promise().map_err(|failure| match failure {
1895 EvalFailure::Runtime(kind) => kind,
1896 _ => RuntimeErrorKind::InvalidValue { value: derived },
1897 })?;
1898 let record = self
1899 .create_promise_finally(derived, value, origin, completion)
1900 .map_err(|failure| match failure {
1901 EvalFailure::Runtime(kind) => kind,
1902 _ => RuntimeErrorKind::InvalidValue { value: derived },
1903 })?;
1904 let (on_fulfilled, on_rejected) = self.intrinsics.builtins.promise_finally_targets();
1905 let on_fulfilled = self
1906 .create_promise_resolver_function(on_fulfilled, record)
1907 .map_err(|failure| match failure {
1908 EvalFailure::Runtime(kind) => kind,
1909 _ => RuntimeErrorKind::InvalidValue { value: record },
1910 })?;
1911 let on_rejected = self
1912 .create_promise_resolver_function(on_rejected, record)
1913 .map_err(|failure| match failure {
1914 EvalFailure::Runtime(kind) => kind,
1915 _ => RuntimeErrorKind::InvalidValue { value: record },
1916 })?;
1917 self.promise_then(cleanup, on_fulfilled, on_rejected)
1918 .map_err(|failure| match failure {
1919 EvalFailure::Runtime(kind) => kind,
1920 _ => RuntimeErrorKind::InvalidValue { value: cleanup },
1921 })?;
1922 match self.call_value(handler, Value::UNDEFINED, &[]) {
1923 Ok(result) => self.resolve_promise(cleanup, result),
1924 Err(EvalFailure::Runtime(kind)) => Err(kind),
1925 Err(failure) => self
1926 .reject_promise_failure(cleanup, failure)
1927 .map_err(|failure| match failure {
1928 EvalFailure::Runtime(kind) => kind,
1929 _ => RuntimeErrorKind::InvalidValue { value: cleanup },
1930 }),
1931 }
1932 }
1933
1934 pub(crate) fn enqueue_microtask_callback(
1935 &mut self,
1936 callback: Value,
1937 ) -> Result<(), EvalFailure> {
1938 self.ensure_microtask_capacity(1)
1939 .map_err(EvalFailure::Runtime)?;
1940 self.microtasks
1941 .push_back(MicrotaskJob::Callback { callback });
1942 Ok(())
1943 }
1944
1945 fn ensure_microtask_capacity(&mut self, additional: usize) -> Result<(), RuntimeErrorKind> {
1946 if self
1947 .microtasks
1948 .len()
1949 .checked_add(additional)
1950 .is_none_or(|length| length > self.limits.max_microtasks)
1951 {
1952 return Err(RuntimeErrorKind::MicrotaskQueueLimitExceeded {
1953 limit: self.limits.max_microtasks,
1954 });
1955 }
1956 self.microtasks.try_reserve(additional).map_err(|_| {
1957 RuntimeErrorKind::HeapByteLimitExceeded {
1958 limit: self.limits.max_heap_bytes,
1959 }
1960 })
1961 }
1962
1963 pub(crate) fn create_promise(&mut self) -> Result<Value, EvalFailure> {
1964 self.allocate(HeapEntry::Promise {
1965 state: PromiseState::Pending {
1966 fulfill_reactions: Vec::new(),
1967 reject_reactions: Vec::new(),
1968 },
1969 properties: PropertyMap::default(),
1970 prototype: Some(self.intrinsics.builtins.promise_prototype()),
1971 extensible: true,
1972 })
1973 .map_err(EvalFailure::Runtime)
1974 }
1975
1976 pub(crate) fn create_promise_resolver(&mut self, promise: Value) -> Result<Value, EvalFailure> {
1977 self.allocate(HeapEntry::PromiseResolver {
1978 promise,
1979 used: false,
1980 })
1981 .map_err(EvalFailure::Runtime)
1982 }
1983
1984 pub(crate) fn create_promise_resolver_function(
1985 &mut self,
1986 target: Value,
1987 record: Value,
1988 ) -> Result<Value, EvalFailure> {
1989 self.allocate(HeapEntry::NativeFunction {
1990 callable: NativeCallable::Bound(Box::new(BoundCallable {
1991 target,
1992 this_value: Value::UNDEFINED,
1993 arguments: vec![record],
1994 })),
1995 properties: PropertyMap::default(),
1996 extensible: true,
1997 })
1998 .map_err(EvalFailure::Runtime)
1999 }
2000
2001 pub(crate) fn resolve_promise_resolver(
2002 &mut self,
2003 record: Value,
2004 value: Value,
2005 ) -> Result<(), EvalFailure> {
2006 if let Some(promise) = self.use_promise_resolver(record)? {
2007 self.resolve_promise(promise, value)
2008 .map_err(EvalFailure::Runtime)?;
2009 }
2010 Ok(())
2011 }
2012
2013 pub(crate) fn reject_promise_resolver(
2014 &mut self,
2015 record: Value,
2016 reason: Value,
2017 ) -> Result<(), EvalFailure> {
2018 if let Some(promise) = self.use_promise_resolver(record)? {
2019 self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2020 .map_err(EvalFailure::Runtime)?;
2021 }
2022 Ok(())
2023 }
2024
2025 pub(crate) fn reject_promise_resolver_failure(
2026 &mut self,
2027 record: Value,
2028 failure: EvalFailure,
2029 ) -> Result<(), EvalFailure> {
2030 if let Some(promise) = self.use_promise_resolver(record)? {
2031 self.reject_promise_failure(promise, failure)?;
2032 }
2033 Ok(())
2034 }
2035
2036 fn use_promise_resolver(&mut self, record: Value) -> Result<Option<Value>, EvalFailure> {
2037 let index = self
2038 .runtime_slot(record)
2039 .map_err(EvalFailure::Runtime)?
2040 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2041 operation: "Promise resolver",
2042 }))?;
2043 let HeapEntry::PromiseResolver { promise, used } = &mut self.heap[index] else {
2044 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2045 operation: "Promise resolver",
2046 }));
2047 };
2048 if *used {
2049 return Ok(None);
2050 }
2051 *used = true;
2052 Ok(Some(*promise))
2053 }
2054
2055 fn charge_promise_reactions(&mut self, count: usize) -> Result<(), EvalFailure> {
2056 let bytes = std::mem::size_of::<PromiseReaction>()
2057 .checked_mul(count)
2058 .ok_or(EvalFailure::Runtime(
2059 RuntimeErrorKind::HeapByteLimitExceeded {
2060 limit: self.limits.max_heap_bytes,
2061 },
2062 ))?;
2063 self.charge_heap(bytes).map_err(EvalFailure::Runtime)
2064 }
2065
2066 pub(crate) fn promise_then(
2067 &mut self,
2068 promise: Value,
2069 on_fulfilled: Value,
2070 on_rejected: Value,
2071 ) -> Result<Value, EvalFailure> {
2072 let index = self
2073 .runtime_slot(promise)
2074 .map_err(EvalFailure::Runtime)?
2075 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2076 operation: "Promise.prototype.then",
2077 }))?;
2078 let settled = match &self.heap[index] {
2079 HeapEntry::Promise {
2080 state: PromiseState::Pending { .. },
2081 ..
2082 } => None,
2083 HeapEntry::Promise {
2084 state: PromiseState::Fulfilled { value },
2085 ..
2086 } => Some((true, *value, ThrowOrigin::Bytecode)),
2087 HeapEntry::Promise {
2088 state: PromiseState::Rejected { reason, origin },
2089 ..
2090 } => Some((false, *reason, *origin)),
2091 _ => {
2092 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2093 operation: "Promise.prototype.then",
2094 }));
2095 }
2096 };
2097 let derived = self.create_promise()?;
2098 if let Some((fulfilled, value, origin)) = settled {
2099 self.ensure_microtask_capacity(1)
2100 .map_err(EvalFailure::Runtime)?;
2101 let reaction = if fulfilled {
2102 PromiseReaction::Fulfilled {
2103 handler: on_fulfilled,
2104 derived,
2105 }
2106 } else {
2107 PromiseReaction::Rejected {
2108 handler: on_rejected,
2109 derived,
2110 }
2111 };
2112 self.microtasks.push_back(MicrotaskJob::Reaction {
2113 reaction,
2114 value,
2115 origin,
2116 });
2117 return Ok(derived);
2118 }
2119 self.charge_promise_reactions(2)?;
2120 let HeapEntry::Promise {
2121 state:
2122 PromiseState::Pending {
2123 fulfill_reactions,
2124 reject_reactions,
2125 },
2126 ..
2127 } = &mut self.heap[index]
2128 else {
2129 unreachable!("pending Promise state was checked before derived allocation");
2130 };
2131 fulfill_reactions.push(PromiseReaction::Fulfilled {
2132 handler: on_fulfilled,
2133 derived,
2134 });
2135 reject_reactions.push(PromiseReaction::Rejected {
2136 handler: on_rejected,
2137 derived,
2138 });
2139 Ok(derived)
2140 }
2141
2142 pub(crate) fn promise_finally(
2143 &mut self,
2144 promise: Value,
2145 handler: Value,
2146 ) -> Result<Value, EvalFailure> {
2147 let index = self
2148 .runtime_slot(promise)
2149 .map_err(EvalFailure::Runtime)?
2150 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2151 operation: "Promise.prototype.finally",
2152 }))?;
2153 let settled = match &self.heap[index] {
2154 HeapEntry::Promise {
2155 state: PromiseState::Pending { .. },
2156 ..
2157 } => None,
2158 HeapEntry::Promise {
2159 state: PromiseState::Fulfilled { value },
2160 ..
2161 } => Some((true, *value, ThrowOrigin::Bytecode)),
2162 HeapEntry::Promise {
2163 state: PromiseState::Rejected { reason, origin },
2164 ..
2165 } => Some((false, *reason, *origin)),
2166 _ => {
2167 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2168 operation: "Promise.prototype.finally",
2169 }));
2170 }
2171 };
2172 let derived = self.create_promise()?;
2173 let reaction = |completion| PromiseReaction::Finally {
2174 handler,
2175 derived,
2176 completion,
2177 };
2178 if let Some((fulfilled, value, origin)) = settled {
2179 self.ensure_microtask_capacity(1)
2180 .map_err(EvalFailure::Runtime)?;
2181 self.microtasks.push_back(MicrotaskJob::Reaction {
2182 reaction: reaction(if fulfilled {
2183 PromiseCompletion::Fulfilled
2184 } else {
2185 PromiseCompletion::Rejected
2186 }),
2187 value,
2188 origin,
2189 });
2190 return Ok(derived);
2191 }
2192 self.charge_promise_reactions(2)?;
2193 let HeapEntry::Promise {
2194 state:
2195 PromiseState::Pending {
2196 fulfill_reactions,
2197 reject_reactions,
2198 },
2199 ..
2200 } = &mut self.heap[index]
2201 else {
2202 unreachable!("pending Promise state was checked before derived allocation");
2203 };
2204 fulfill_reactions.push(reaction(PromiseCompletion::Fulfilled));
2205 reject_reactions.push(reaction(PromiseCompletion::Rejected));
2206 Ok(derived)
2207 }
2208
2209 pub(crate) fn create_promise_finally(
2210 &mut self,
2211 derived: Value,
2212 value: Value,
2213 origin: ThrowOrigin,
2214 completion: PromiseCompletion,
2215 ) -> Result<Value, EvalFailure> {
2216 self.allocate(HeapEntry::PromiseFinally {
2217 derived,
2218 value,
2219 origin,
2220 completion,
2221 })
2222 .map_err(EvalFailure::Runtime)
2223 }
2224
2225 pub(crate) fn fulfill_promise_finally(&mut self, record: Value) -> Result<(), EvalFailure> {
2226 let (derived, value, origin, completion) = self.promise_finally_record(record)?;
2227 match completion {
2228 PromiseCompletion::Fulfilled => self
2229 .resolve_promise(derived, value)
2230 .map_err(EvalFailure::Runtime),
2231 PromiseCompletion::Rejected => self
2232 .reject_promise(derived, value, origin)
2233 .map_err(EvalFailure::Runtime),
2234 }
2235 }
2236
2237 pub(crate) fn reject_promise_finally(
2238 &mut self,
2239 record: Value,
2240 reason: Value,
2241 ) -> Result<(), EvalFailure> {
2242 let (derived, _, _, _) = self.promise_finally_record(record)?;
2243 self.reject_promise(derived, reason, ThrowOrigin::Bytecode)
2244 .map_err(EvalFailure::Runtime)
2245 }
2246
2247 fn promise_finally_record(
2248 &mut self,
2249 record: Value,
2250 ) -> Result<(Value, Value, ThrowOrigin, PromiseCompletion), EvalFailure> {
2251 let index = self
2252 .runtime_slot(record)
2253 .map_err(EvalFailure::Runtime)?
2254 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2255 operation: "Promise finally target",
2256 }))?;
2257 let HeapEntry::PromiseFinally {
2258 derived,
2259 value,
2260 origin,
2261 completion,
2262 } = &self.heap[index]
2263 else {
2264 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2265 operation: "Promise finally target",
2266 }));
2267 };
2268 Ok((*derived, *value, *origin, *completion))
2269 }
2270
2271 pub(crate) fn promise_resolve(&mut self, value: Value) -> Result<Value, EvalFailure> {
2272 if matches!(self.runtime_slot(value).map_err(EvalFailure::Runtime)?, Some(index) if matches!(self.heap[index], HeapEntry::Promise { .. }))
2273 {
2274 return Ok(value);
2275 }
2276 let promise = self.create_promise()?;
2277 self.resolve_promise(promise, value)
2278 .map_err(EvalFailure::Runtime)?;
2279 Ok(promise)
2280 }
2281
2282 pub(crate) fn promise_reject(&mut self, reason: Value) -> Result<Value, EvalFailure> {
2283 let promise = self.create_promise()?;
2284 self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2285 .map_err(EvalFailure::Runtime)?;
2286 Ok(promise)
2287 }
2288
2289 pub(crate) fn promise_all(&mut self, iterable: Value) -> Result<Value, EvalFailure> {
2290 let promise = self.create_promise()?;
2291 let aggregate = self
2292 .allocate(HeapEntry::PromiseAll {
2293 promise,
2294 values: Vec::new(),
2295 remaining: 1,
2296 settled: false,
2297 })
2298 .map_err(EvalFailure::Runtime)?;
2299 let iterator = match self.create_iterator(iterable, IteratorKind::Sync) {
2300 Ok(iterator) => iterator,
2301 Err(failure) => {
2302 self.mark_promise_all_settled(aggregate)?;
2303 self.reject_promise_failure(promise, failure)?;
2304 return Ok(promise);
2305 }
2306 };
2307 loop {
2308 let value = match self.iterator_next(iterator) {
2309 Ok((true, _)) => break,
2310 Ok((false, value)) => value,
2311 Err(failure) => {
2312 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2313 }
2314 };
2315 let index = match self.add_promise_all_element(aggregate) {
2316 Ok(index) => index,
2317 Err(failure) => {
2318 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2319 }
2320 };
2321 let element = match self
2322 .allocate(HeapEntry::PromiseAllElement {
2323 aggregate,
2324 index,
2325 called: false,
2326 })
2327 .map_err(EvalFailure::Runtime)
2328 {
2329 Ok(element) => element,
2330 Err(failure) => {
2331 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2332 }
2333 };
2334 let (fulfill_target, reject_target) = self.intrinsics.builtins.promise_all_targets();
2335 let on_fulfilled = match self.create_promise_resolver_function(fulfill_target, element)
2336 {
2337 Ok(callback) => callback,
2338 Err(failure) => {
2339 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2340 }
2341 };
2342 let on_rejected = match self.create_promise_resolver_function(reject_target, element) {
2343 Ok(callback) => callback,
2344 Err(failure) => {
2345 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2346 }
2347 };
2348 let resolved = match self.promise_resolve(value) {
2349 Ok(resolved) => resolved,
2350 Err(failure) => {
2351 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2352 }
2353 };
2354 if let Err(failure) = self.promise_then(resolved, on_fulfilled, on_rejected) {
2355 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2356 }
2357 }
2358 if let Some(values) = self.finish_promise_all(aggregate)? {
2359 let array = self.create_array(values)?;
2360 self.fulfill_promise(promise, array)
2361 .map_err(EvalFailure::Runtime)?;
2362 }
2363 Ok(promise)
2364 }
2365
2366 fn reject_promise_all_abrupt(
2367 &mut self,
2368 aggregate: Value,
2369 promise: Value,
2370 iterator: Value,
2371 failure: EvalFailure,
2372 ) -> Result<Value, EvalFailure> {
2373 self.mark_promise_all_settled(aggregate)?;
2374 if let Err(EvalFailure::Runtime(kind)) = self.close_iterator(iterator) {
2375 return Err(EvalFailure::Runtime(kind));
2376 }
2377 self.reject_promise_failure(promise, failure)?;
2378 Ok(promise)
2379 }
2380
2381 fn close_iterator(&mut self, iterator: Value) -> Result<(), EvalFailure> {
2382 let Some(index) = self.runtime_slot(iterator).map_err(EvalFailure::Runtime)? else {
2383 return Ok(());
2384 };
2385 let HeapEntry::Iterator {
2386 state: IteratorState::Protocol { iterator, .. },
2387 } = &self.heap[index]
2388 else {
2389 return Ok(());
2390 };
2391 let iterator = *iterator;
2392 let close = self.get_named_property(iterator, "return")?;
2393 if self.is_callable(close)? {
2394 let _ = self.call_value(close, iterator, &[])?;
2395 }
2396 Ok(())
2397 }
2398
2399 fn mark_promise_all_settled(&mut self, aggregate: Value) -> Result<bool, EvalFailure> {
2400 let index = self
2401 .runtime_slot(aggregate)
2402 .map_err(EvalFailure::Runtime)?
2403 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2404 operation: "Promise.all target",
2405 }))?;
2406 let HeapEntry::PromiseAll { settled, .. } = &mut self.heap[index] else {
2407 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2408 operation: "Promise.all target",
2409 }));
2410 };
2411 let changed = !*settled;
2412 *settled = true;
2413 Ok(changed)
2414 }
2415
2416 fn add_promise_all_element(&mut self, aggregate: Value) -> Result<usize, EvalFailure> {
2417 let index = self
2418 .runtime_slot(aggregate)
2419 .map_err(EvalFailure::Runtime)?
2420 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2421 operation: "Promise.all target",
2422 }))?;
2423 let next_remaining = match &self.heap[index] {
2424 HeapEntry::PromiseAll {
2425 remaining,
2426 settled: false,
2427 ..
2428 } => remaining.checked_add(1).ok_or(EvalFailure::Runtime(
2429 RuntimeErrorKind::HeapByteLimitExceeded {
2430 limit: self.limits.max_heap_bytes,
2431 },
2432 ))?,
2433 HeapEntry::PromiseAll { .. } => {
2434 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2435 operation: "Promise.all target",
2436 }));
2437 }
2438 _ => {
2439 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2440 operation: "Promise.all target",
2441 }));
2442 }
2443 };
2444 self.charge_heap(std::mem::size_of::<Value>())
2445 .map_err(EvalFailure::Runtime)?;
2446 let HeapEntry::PromiseAll {
2447 values, remaining, ..
2448 } = &mut self.heap[index]
2449 else {
2450 unreachable!("Promise.all aggregate was checked before its heap charge");
2451 };
2452 values.try_reserve(1).map_err(|_| {
2453 EvalFailure::Runtime(RuntimeErrorKind::HeapByteLimitExceeded {
2454 limit: self.limits.max_heap_bytes,
2455 })
2456 })?;
2457 let index = values.len();
2458 values.push(Value::UNDEFINED);
2459 *remaining = next_remaining;
2460 Ok(index)
2461 }
2462
2463 fn finish_promise_all(&mut self, aggregate: Value) -> Result<Option<Vec<Value>>, EvalFailure> {
2464 let index = self
2465 .runtime_slot(aggregate)
2466 .map_err(EvalFailure::Runtime)?
2467 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2468 operation: "Promise.all target",
2469 }))?;
2470 let HeapEntry::PromiseAll {
2471 values,
2472 remaining,
2473 settled,
2474 ..
2475 } = &mut self.heap[index]
2476 else {
2477 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2478 operation: "Promise.all target",
2479 }));
2480 };
2481 if *settled {
2482 return Ok(None);
2483 }
2484 *remaining -= 1;
2485 if *remaining != 0 {
2486 return Ok(None);
2487 }
2488 *settled = true;
2489 Ok(Some(std::mem::take(values)))
2490 }
2491
2492 pub(crate) fn resolve_promise_all_element(
2493 &mut self,
2494 element: Value,
2495 value: Value,
2496 ) -> Result<(), EvalFailure> {
2497 let index = self
2498 .runtime_slot(element)
2499 .map_err(EvalFailure::Runtime)?
2500 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2501 operation: "Promise.all target",
2502 }))?;
2503 let (aggregate, output_index) = {
2504 let HeapEntry::PromiseAllElement {
2505 aggregate,
2506 index: output_index,
2507 called,
2508 } = &mut self.heap[index]
2509 else {
2510 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2511 operation: "Promise.all target",
2512 }));
2513 };
2514 if *called {
2515 return Ok(());
2516 }
2517 *called = true;
2518 (*aggregate, *output_index)
2519 };
2520 let aggregate_index = self
2521 .runtime_slot(aggregate)
2522 .map_err(EvalFailure::Runtime)?
2523 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2524 operation: "Promise.all target",
2525 }))?;
2526 let (promise, values) = {
2527 let HeapEntry::PromiseAll {
2528 promise,
2529 values,
2530 remaining,
2531 settled,
2532 } = &mut self.heap[aggregate_index]
2533 else {
2534 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2535 operation: "Promise.all target",
2536 }));
2537 };
2538 if *settled {
2539 return Ok(());
2540 }
2541 values[output_index] = value;
2542 *remaining -= 1;
2543 let values = (*remaining == 0).then(|| {
2544 *settled = true;
2545 std::mem::take(values)
2546 });
2547 (*promise, values)
2548 };
2549 if let Some(values) = values {
2550 let array = self.create_array(values)?;
2551 self.fulfill_promise(promise, array)
2552 .map_err(EvalFailure::Runtime)?;
2553 }
2554 Ok(())
2555 }
2556
2557 pub(crate) fn reject_promise_all_element(
2558 &mut self,
2559 element: Value,
2560 reason: Value,
2561 ) -> Result<(), EvalFailure> {
2562 let index = self
2563 .runtime_slot(element)
2564 .map_err(EvalFailure::Runtime)?
2565 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2566 operation: "Promise.all target",
2567 }))?;
2568 let aggregate = {
2569 let HeapEntry::PromiseAllElement {
2570 aggregate, called, ..
2571 } = &mut self.heap[index]
2572 else {
2573 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2574 operation: "Promise.all target",
2575 }));
2576 };
2577 if *called {
2578 return Ok(());
2579 }
2580 *called = true;
2581 *aggregate
2582 };
2583 let aggregate_index = self
2584 .runtime_slot(aggregate)
2585 .map_err(EvalFailure::Runtime)?
2586 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2587 operation: "Promise.all target",
2588 }))?;
2589 let HeapEntry::PromiseAll { promise, .. } = &self.heap[aggregate_index] else {
2590 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2591 operation: "Promise.all target",
2592 }));
2593 };
2594 let promise = *promise;
2595 if !self.mark_promise_all_settled(aggregate)? {
2596 return Ok(());
2597 }
2598 self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2599 .map_err(EvalFailure::Runtime)
2600 }
2601
2602 fn create_array(&mut self, elements: Vec<Value>) -> Result<Value, EvalFailure> {
2603 self.allocate(HeapEntry::Array {
2604 elements,
2605 properties: PropertyMap::default(),
2606 prototype: Some(self.intrinsics.array_prototype),
2607 extensible: true,
2608 length_writable: true,
2609 })
2610 .map_err(EvalFailure::Runtime)
2611 }
2612
2613 fn resolve_promise(&mut self, promise: Value, value: Value) -> Result<(), RuntimeErrorKind> {
2614 if promise == value {
2615 return self
2616 .reject_promise_failure(
2617 promise,
2618 EvalFailure::Throw(ThrowOrigin::TypeError {
2619 operation: "Promise cannot resolve itself",
2620 }),
2621 )
2622 .map_err(|failure| match failure {
2623 EvalFailure::Runtime(kind) => kind,
2624 _ => RuntimeErrorKind::InvalidValue { value: promise },
2625 });
2626 }
2627 if !self.is_object(value) {
2628 return self.fulfill_promise(promise, value);
2629 }
2630 let then = match self.get_named_property(value, "then") {
2631 Ok(then) => then,
2632 Err(EvalFailure::Runtime(kind)) => return Err(kind),
2633 Err(failure) => {
2634 return self.reject_promise_failure(promise, failure).map_err(
2635 |failure| match failure {
2636 EvalFailure::Runtime(kind) => kind,
2637 _ => RuntimeErrorKind::InvalidValue { value: promise },
2638 },
2639 );
2640 }
2641 };
2642 if !self.is_callable(then).map_err(|failure| match failure {
2643 EvalFailure::Runtime(kind) => kind,
2644 _ => RuntimeErrorKind::InvalidValue { value: then },
2645 })? {
2646 return self.fulfill_promise(promise, value);
2647 }
2648 self.ensure_microtask_capacity(1)?;
2649 self.microtasks.push_back(MicrotaskJob::Thenable {
2650 promise,
2651 thenable: value,
2652 then,
2653 });
2654 Ok(())
2655 }
2656
2657 fn reject_promise(
2658 &mut self,
2659 promise: Value,
2660 reason: Value,
2661 origin: ThrowOrigin,
2662 ) -> Result<(), RuntimeErrorKind> {
2663 self.settle_promise(promise, PromiseState::Rejected { reason, origin })
2664 }
2665
2666 fn fulfill_promise(&mut self, promise: Value, value: Value) -> Result<(), RuntimeErrorKind> {
2667 self.settle_promise(promise, PromiseState::Fulfilled { value })
2668 }
2669
2670 fn settle_promise(
2671 &mut self,
2672 promise: Value,
2673 terminal: PromiseState,
2674 ) -> Result<(), RuntimeErrorKind> {
2675 let index = self
2676 .runtime_slot(promise)?
2677 .ok_or(RuntimeErrorKind::InvalidValue { value: promise })?;
2678 let reaction_count = match &self.heap[index] {
2679 HeapEntry::Promise {
2680 state:
2681 PromiseState::Pending {
2682 fulfill_reactions,
2683 reject_reactions,
2684 },
2685 ..
2686 } => match &terminal {
2687 PromiseState::Fulfilled { .. } => fulfill_reactions.len(),
2688 PromiseState::Rejected { .. } => reject_reactions.len(),
2689 PromiseState::Pending { .. } => unreachable!("Promise settlement is terminal"),
2690 },
2691 HeapEntry::Promise { .. } => return Ok(()),
2692 _ => return Err(RuntimeErrorKind::InvalidValue { value: promise }),
2693 };
2694 self.ensure_microtask_capacity(reaction_count)?;
2695 let reactions = match &mut self.heap[index] {
2696 HeapEntry::Promise { state, .. } => {
2697 let reactions = match state {
2698 PromiseState::Pending {
2699 fulfill_reactions,
2700 reject_reactions,
2701 } => match &terminal {
2702 PromiseState::Fulfilled { .. } => std::mem::take(fulfill_reactions),
2703 PromiseState::Rejected { .. } => std::mem::take(reject_reactions),
2704 PromiseState::Pending { .. } => {
2705 unreachable!("Promise settlement is terminal")
2706 }
2707 },
2708 _ => return Ok(()),
2709 };
2710 *state = terminal.clone();
2711 reactions
2712 }
2713 _ => return Err(RuntimeErrorKind::InvalidValue { value: promise }),
2714 };
2715 let (value, origin) = match terminal {
2716 PromiseState::Fulfilled { value } => (value, ThrowOrigin::Bytecode),
2717 PromiseState::Rejected { reason, origin } => (reason, origin),
2718 PromiseState::Pending { .. } => unreachable!("Promise settlement is terminal"),
2719 };
2720 for reaction in reactions {
2721 self.microtasks.push_back(MicrotaskJob::Reaction {
2722 reaction,
2723 value,
2724 origin,
2725 });
2726 }
2727 Ok(())
2728 }
2729
2730 fn reject_promise_failure(
2731 &mut self,
2732 promise: Value,
2733 failure: EvalFailure,
2734 ) -> Result<(), EvalFailure> {
2735 let (reason, origin) = self.promise_rejection_value(failure)?;
2736 self.reject_promise(promise, reason, origin)
2737 .map_err(EvalFailure::Runtime)
2738 }
2739
2740 fn promise_rejection_value(
2741 &mut self,
2742 failure: EvalFailure,
2743 ) -> Result<(Value, ThrowOrigin), EvalFailure> {
2744 match failure {
2745 EvalFailure::ThrowValue(value) => Ok((value, ThrowOrigin::Bytecode)),
2746 EvalFailure::ThrowValueOrigin { value, origin } => Ok((value, origin)),
2747 EvalFailure::Throw(ThrowOrigin::Bytecode) => {
2748 Ok((Value::UNDEFINED, ThrowOrigin::Bytecode))
2749 }
2750 EvalFailure::Throw(origin) => {
2751 let (name, message) = match origin {
2752 ThrowOrigin::TypeError { operation } => ("TypeError", operation),
2753 ThrowOrigin::RangeError { operation } => ("RangeError", operation),
2754 ThrowOrigin::ReferenceError { operation } => ("ReferenceError", operation),
2755 ThrowOrigin::UriError { operation } => ("URIError", operation),
2756 ThrowOrigin::Bytecode => unreachable!("handled above"),
2757 };
2758 let id = self
2759 .intrinsics
2760 .builtins
2761 .id_named(name)
2762 .expect("error constructor is installed");
2763 match self.throw_error(id, message.to_owned()) {
2764 EvalFailure::ThrowValue(value) => Ok((value, origin)),
2765 EvalFailure::Runtime(kind) => Err(EvalFailure::Runtime(kind)),
2766 _ => unreachable!("error materialization returns a thrown value"),
2767 }
2768 }
2769 EvalFailure::Runtime(kind) => Err(EvalFailure::Runtime(kind)),
2770 }
2771 }
2772
2773 fn program(&self) -> &Program<Verified> {
2774 self.program
2775 .expect("module registry operations require a whole program")
2776 }
2777
2778 fn module_code(&self, module: ModuleId) -> &Module<Verified> {
2779 let index = module.get() as usize;
2780 if index >= self.dynamic_base {
2781 return &self.dynamic[index - self.dynamic_base].program.modules()[0].code;
2782 }
2783 match self.program {
2784 Some(program) => {
2785 &program
2786 .module(module)
2787 .expect("verified module id remains in bounds")
2788 .code
2789 }
2790 None => self.module,
2791 }
2792 }
2793
2794 fn program_module(&self, module: ModuleId) -> &ProgramModule<Verified> {
2795 let index = module.get() as usize;
2796 if index >= self.dynamic_base {
2797 return &self.dynamic[index - self.dynamic_base].program.modules()[0];
2798 }
2799 self.program
2800 .and_then(|program| program.module(module))
2801 .expect("verified module id remains in bounds")
2802 }
2803
2804 fn validate_dynamic_script(program: &Program<Verified>) -> Result<(), &'static str> {
2806 if program.modules().len() != 1 {
2807 return Err("script program must contain exactly one module");
2808 }
2809 if program.entry() != ModuleId::new(0) {
2810 return Err("script program entry must be module zero");
2811 }
2812 let module = &program.modules()[0];
2813 if !module.edges.is_empty() || !module.bindings.is_empty() || !module.exports.is_empty() {
2814 return Err("script program must not contain linkage metadata");
2815 }
2816 if module
2817 .code
2818 .functions()
2819 .iter()
2820 .flat_map(|function| function.code())
2821 .any(|instruction| {
2822 matches!(
2823 instruction,
2824 Instruction::Import { .. } | Instruction::Export { .. }
2825 )
2826 })
2827 {
2828 return Err("script program must not contain import or export instructions");
2829 }
2830 Ok(())
2831 }
2832
2833 fn script_heap_cost(program: &Program<Verified>) -> usize {
2834 const MODULE_BYTES: usize = 64;
2835 const FUNCTION_BYTES: usize = 32;
2836 program.modules().iter().fold(0usize, |total, module| {
2837 let constant_bytes = module
2838 .code
2839 .constants()
2840 .iter()
2841 .fold(0usize, |bytes, constant| {
2842 let payload = match constant {
2843 Constant::String(text) => text.len_units().saturating_mul(2),
2844 Constant::BigInt(value) => value.as_str().len(),
2845 Constant::Number(_)
2846 | Constant::Int32(_)
2847 | Constant::Boolean(_)
2848 | Constant::Null
2849 | Constant::Undefined => 0,
2850 };
2851 bytes
2852 .saturating_add(std::mem::size_of::<Constant>())
2853 .saturating_add(payload)
2854 });
2855 let function_bytes =
2856 module
2857 .code
2858 .functions()
2859 .iter()
2860 .fold(0usize, |bytes, function| {
2861 bytes
2862 .saturating_add(FUNCTION_BYTES)
2863 .saturating_add(
2864 function
2865 .code()
2866 .len()
2867 .saturating_mul(std::mem::size_of::<Instruction>()),
2868 )
2869 .saturating_add(function.handlers().len().saturating_mul(
2870 std::mem::size_of::<bamts_bytecode::ExceptionHandler>(),
2871 ))
2872 });
2873 total
2874 .saturating_add(MODULE_BYTES)
2875 .saturating_add(constant_bytes)
2876 .saturating_add(function_bytes)
2877 .saturating_add(module.code.verification_bytes())
2878 })
2879 }
2880
2881 fn install_script_reserving(
2882 &mut self,
2883 program: Arc<Program<Verified>>,
2884 reserved_slots: usize,
2885 reserved_bytes: usize,
2886 ) -> Result<ModuleId, RuntimeErrorKind> {
2887 Self::validate_dynamic_script(&program)
2888 .map_err(|reason| RuntimeErrorKind::InvalidDynamicScript { reason })?;
2889 if self.dynamic.len() >= self.limits.max_dynamic_modules {
2890 return Err(RuntimeErrorKind::DynamicModuleLimitExceeded {
2891 limit: self.limits.max_dynamic_modules,
2892 });
2893 }
2894 let bytes = Self::script_heap_cost(&program);
2895 let retained_bytes =
2896 bytes
2897 .checked_add(reserved_bytes)
2898 .ok_or(RuntimeErrorKind::HeapByteLimitExceeded {
2899 limit: self.limits.max_heap_bytes,
2900 })?;
2901 self.ensure_allocation_capacity(reserved_slots, retained_bytes)?;
2902 self.charge_heap(bytes)?;
2903 let index = self.dynamic_base.checked_add(self.dynamic.len()).ok_or(
2904 RuntimeErrorKind::DynamicModuleLimitExceeded {
2905 limit: self.limits.max_dynamic_modules,
2906 },
2907 )?;
2908 let module = ModuleId::new(u32::try_from(index).map_err(|_| {
2909 RuntimeErrorKind::DynamicModuleLimitExceeded {
2910 limit: self.limits.max_dynamic_modules,
2911 }
2912 })?);
2913 self.dynamic.push(DynamicModule { program, bytes });
2914 self.registry.modules.push(ModuleInstance {
2915 binding_cells: Vec::new(),
2916 constant_cells: Vec::new(),
2917 namespace: None,
2918 state: ModuleState::Unevaluated,
2919 });
2920 debug_assert_eq!(
2921 self.dynamic
2922 .last()
2923 .expect("installed script remains retained")
2924 .bytes,
2925 bytes
2926 );
2927 debug_assert_eq!(
2928 self.registry.modules.len(),
2929 self.dynamic_base + self.dynamic.len()
2930 );
2931 Ok(module)
2932 }
2933
2934 fn allocate_cell(&mut self, value: Value, module: ModuleId) -> Result<CellId, RuntimeError> {
2935 if self.registry.cells.len() >= self.limits.max_module_cells {
2936 return Err(self.program_error(
2937 module,
2938 RuntimeErrorKind::ModuleCellLimitExceeded {
2939 limit: self.limits.max_module_cells,
2940 },
2941 ));
2942 }
2943 let id = CellId(self.registry.cells.len());
2944 self.registry.cells.push(Cell { value });
2945 Ok(id)
2946 }
2947
2948 pub(crate) fn instantiate_modules(&mut self) -> Result<(), RuntimeError> {
2949 debug_assert!(
2950 self.dynamic.is_empty(),
2951 "module instantiation precedes dynamic script installation"
2952 );
2953 let program = self
2954 .program
2955 .expect("module registry operations require a whole program");
2956 self.registry.modules = program
2957 .modules()
2958 .iter()
2959 .map(|module| ModuleInstance {
2960 binding_cells: vec![None; module.bindings.len()],
2961 constant_cells: vec![None; module.code.constants().len()],
2962 namespace: None,
2963 state: ModuleState::Unevaluated,
2964 })
2965 .collect();
2966
2967 for module_index in 0..program.modules().len() {
2968 let module_id = ModuleId::new(module_index as u32);
2969 let bindings = program.modules()[module_index].bindings.clone();
2970 for (binding_index, binding) in bindings.into_iter().enumerate() {
2971 let initial = match binding.kind {
2972 BindingKind::Hoisted => Some(Value::UNDEFINED),
2973 BindingKind::Lexical => Some(Value::UNINITIALIZED),
2974 BindingKind::Imported { .. } | BindingKind::Namespace { .. } => None,
2975 };
2976 if let Some(value) = initial {
2977 let cell = self.allocate_cell(value, module_id)?;
2978 self.registry.modules[module_index].binding_cells[binding_index] = Some(cell);
2979 }
2980 }
2981 }
2982
2983 for module_index in 0..program.modules().len() {
2984 let module_id = ModuleId::new(module_index as u32);
2985 let bindings = program.modules()[module_index].bindings.clone();
2986 for (binding_index, binding) in bindings.into_iter().enumerate() {
2987 let cell = match binding.kind {
2988 BindingKind::Hoisted | BindingKind::Lexical => continue,
2989 BindingKind::Imported { edge, name } => {
2990 let dependency = program.modules()[module_index].edges[edge.get() as usize];
2991 match dependency.target {
2992 EdgeTarget::External => {
2993 let name = self.constant_text(module_id, name).clone();
2994 self.external_export_cell(module_id, edge, &name)?
2995 }
2996 EdgeTarget::Local(target) => match program
2997 .resolve_export(target, self.constant_text(module_id, name))
2998 {
2999 Some(ResolvedExport::Local { module, binding }) => {
3000 self.registry.modules[module.get() as usize].binding_cells
3001 [binding.get() as usize]
3002 .expect("own cells are allocated before aliases link")
3003 }
3004 Some(ResolvedExport::External { module, edge, name }) => {
3005 let name = self.constant_text(module, name).clone();
3006 self.external_export_cell(module, edge, &name)?
3007 }
3008 None => {
3009 return Err(self.program_error(
3010 module_id,
3011 RuntimeErrorKind::InvalidVerifiedProgram {
3012 module: module_id,
3013 instruction: Instruction::Import {
3014 dst: bamts_bytecode::Register::new(0),
3015 specifier: name,
3016 },
3017 },
3018 ));
3019 }
3020 },
3021 }
3022 }
3023 BindingKind::Namespace { edge } => {
3024 let dependency = program.modules()[module_index].edges[edge.get() as usize];
3025 let namespace = match dependency.target {
3026 EdgeTarget::Local(target) => {
3027 self.module_namespace(target, module_id)?
3028 }
3029 EdgeTarget::External => self.external_namespace(module_id, edge)?,
3030 };
3031 self.allocate_cell(namespace, module_id)?
3032 }
3033 };
3034 self.registry.modules[module_index].binding_cells[binding_index] = Some(cell);
3035 }
3036 }
3037
3038 for module_index in 0..program.modules().len() {
3039 let bindings = &program.modules()[module_index].bindings;
3040 let constants = program.modules()[module_index].code.constants();
3041 for (constant_index, constant) in constants.iter().enumerate() {
3042 let Constant::String(name) = constant else {
3043 continue;
3044 };
3045 if let Some((binding_index, _)) =
3046 bindings.iter().enumerate().find(|(_, binding)| {
3047 self.constant_text(ModuleId::new(module_index as u32), binding.name) == name
3048 })
3049 {
3050 self.registry.modules[module_index].constant_cells[constant_index] =
3051 self.registry.modules[module_index].binding_cells[binding_index];
3052 }
3053 }
3054 }
3055 Ok(())
3056 }
3057
3058 fn module_namespace(
3059 &mut self,
3060 target: ModuleId,
3061 requester: ModuleId,
3062 ) -> Result<Value, RuntimeError> {
3063 if let Some(value) = self.registry.modules[target.get() as usize].namespace {
3064 return Ok(value);
3065 }
3066 let exported_names: Vec<EcmaString> = self
3067 .program_module(target)
3068 .exports
3069 .iter()
3070 .map(|export| self.constant_text(target, export.name).clone())
3071 .collect();
3072 for exported_name in exported_names {
3073 if let Some(ResolvedExport::External { module, edge, name }) =
3074 self.program().resolve_export(target, &exported_name)
3075 {
3076 let name = self.constant_text(module, name).clone();
3077 self.external_export_cell(module, edge, &name)?;
3078 }
3079 }
3080 let value = self
3081 .allocate(HeapEntry::ModuleNamespace { module: target })
3082 .map_err(|kind| self.program_error(requester, kind))?;
3083 self.registry.modules[target.get() as usize].namespace = Some(value);
3084 Ok(value)
3085 }
3086
3087 fn external_specifier(&self, module: ModuleId, edge: EdgeId) -> Option<EcmaString> {
3088 let dependency = self.program_module(module).edges[edge.get() as usize];
3089 let specifier = self.constant_text(module, dependency.specifier);
3090 self.registry
3091 .external
3092 .contains_key(specifier)
3093 .then(|| specifier.clone())
3094 }
3095
3096 fn external_namespace(
3097 &mut self,
3098 module: ModuleId,
3099 edge: EdgeId,
3100 ) -> Result<Value, RuntimeError> {
3101 let Some(specifier) = self.external_specifier(module, edge) else {
3102 return Err(self.program_error(
3103 module,
3104 RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3105 ));
3106 };
3107 let export_names: Vec<EcmaString> = self.registry.external[&specifier]
3108 .exports
3109 .keys()
3110 .cloned()
3111 .collect();
3112 for name in export_names {
3113 self.external_export_cell(module, edge, &name)?;
3114 }
3115 Ok(self.registry.external[&specifier].namespace)
3116 }
3117
3118 fn external_export_cell(
3119 &mut self,
3120 module: ModuleId,
3121 edge: EdgeId,
3122 name: &EcmaString,
3123 ) -> Result<CellId, RuntimeError> {
3124 let Some(specifier) = self.external_specifier(module, edge) else {
3125 return Err(self.program_error(
3126 module,
3127 RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3128 ));
3129 };
3130 let Some(export) = self.registry.external[&specifier]
3131 .exports
3132 .get(name)
3133 .copied()
3134 else {
3135 return Err(self.program_error(
3136 module,
3137 RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3138 ));
3139 };
3140 if let Some(cell) = export.cell {
3141 return Ok(cell);
3142 }
3143 let cell = self.allocate_cell(export.value, module)?;
3144 self.registry
3145 .external
3146 .get_mut(&specifier)
3147 .expect("external module remains registered")
3148 .exports
3149 .get_mut(name)
3150 .expect("external export remains registered")
3151 .cell = Some(cell);
3152 Ok(cell)
3153 }
3154
3155 pub(crate) fn resolve_import(
3156 &self,
3157 module: ModuleId,
3158 specifier: ConstantId,
3159 ) -> Result<ImportTarget, RuntimeErrorKind> {
3160 let name = self.constant_text(module, specifier);
3161 self.program_module(module)
3162 .edges
3163 .iter()
3164 .enumerate()
3165 .find(|(_, edge)| {
3166 edge.kind.has_dynamic() && self.constant_text(module, edge.specifier) == name
3167 })
3168 .map(|(index, edge)| match edge.target {
3169 EdgeTarget::Local(target) => ImportTarget::Local(target),
3170 EdgeTarget::External => ImportTarget::External(EdgeId::new(index as u32)),
3171 })
3172 .ok_or(RuntimeErrorKind::DynamicImportEdgeMissing { module, specifier })
3173 }
3174
3175 pub(crate) fn imported_namespace(
3176 &mut self,
3177 requester: ModuleId,
3178 target: ImportTarget,
3179 ) -> Result<Value, RuntimeErrorKind> {
3180 match target {
3181 ImportTarget::Local(target) => self.module_namespace(target, requester),
3182 ImportTarget::External(edge) => self.external_namespace(requester, edge),
3183 }
3184 .map_err(|error| error.kind)
3185 }
3186
3187 fn run_import_entry(&mut self, module: ModuleId) -> Result<(), RuntimeError> {
3188 let function = self.module_code(module).entry();
3189 let stop_depth = self.frames.len();
3190 self.push_frame(
3191 RuntimeFunction { module, function },
3192 &[],
3193 Value::UNDEFINED,
3194 Value::UNDEFINED,
3195 &[],
3196 None,
3197 )?;
3198 let result = self.run_loop(stop_depth).and_then(|execution| {
3199 execution.map(|_| ()).ok_or_else(|| {
3200 self.program_error(
3201 module,
3202 RuntimeErrorKind::InvalidVerifiedProgram {
3203 module,
3204 instruction: Instruction::Halt,
3205 },
3206 )
3207 })
3208 });
3209 if result.is_err() {
3210 self.unwind_frames_to(stop_depth);
3211 }
3212 result
3213 }
3214
3215 fn evaluate_import(&mut self, module: ModuleId) -> Result<(), RuntimeError> {
3216 let dependencies = match self.begin_module_evaluation(module)? {
3217 ModuleEvaluation::Cycle => return Ok(()),
3218 ModuleEvaluation::Evaluated(result) => return result,
3219 ModuleEvaluation::Ready(dependencies) => dependencies,
3220 };
3221 for dependency in dependencies {
3222 if let Err(error) = self.evaluate_import(dependency) {
3223 self.settle_module_evaluation(module, Err(error.clone()));
3224 return Err(error);
3225 }
3226 }
3227 let result = self.run_import_entry(module);
3228 self.settle_module_evaluation(module, result.clone());
3229 result
3230 }
3231
3232 fn import_namespace(
3233 &mut self,
3234 requester: ModuleId,
3235 specifier: ConstantId,
3236 ) -> Result<Value, EvalFailure> {
3237 let target = self
3238 .resolve_import(requester, specifier)
3239 .map_err(EvalFailure::Runtime)?;
3240 if let ImportTarget::Local(module) = target {
3241 self.evaluate_import(module)
3242 .map_err(|error| import_failure(&error))?;
3243 }
3244 self.imported_namespace(requester, target)
3245 .map_err(EvalFailure::Runtime)
3246 }
3247 fn evaluate_module(&mut self, module: ModuleId) -> Result<Option<Execution>, RuntimeError> {
3248 let dependencies = match self.begin_module_evaluation(module)? {
3249 ModuleEvaluation::Cycle => return Ok(None),
3250 ModuleEvaluation::Evaluated(result) => return result.map(|()| None),
3251 ModuleEvaluation::Ready(dependencies) => dependencies,
3252 };
3253 for dependency in dependencies {
3254 if let Err(error) = self.evaluate_module(dependency) {
3255 return self.finish_module_evaluation(module, Err(error)).map(Some);
3256 }
3257 }
3258
3259 let code = self.module_code(module);
3260 let function = code.entry().get() as usize;
3261 let metadata = &code.functions()[function];
3262 let register_count = metadata.register_count() as usize;
3263 let result = if self.limits.max_call_depth < 1 {
3264 Err(self.program_error(
3265 module,
3266 RuntimeErrorKind::CallDepthExceeded {
3267 limit: self.limits.max_call_depth,
3268 },
3269 ))
3270 } else if register_count > self.limits.max_total_registers {
3271 Err(self.program_error(
3272 module,
3273 RuntimeErrorKind::RegisterLimitExceeded {
3274 limit: self.limits.max_total_registers,
3275 },
3276 ))
3277 } else {
3278 self.frames.push(Frame::new(
3279 RuntimeFunction {
3280 module,
3281 function: FunctionId::new(function as u32),
3282 },
3283 metadata,
3284 &[],
3285 Value::UNDEFINED,
3286 Value::UNDEFINED,
3287 &[],
3288 None,
3289 ));
3290 self.live_registers = register_count;
3291 self.run_loop(0).and_then(|execution| {
3292 execution.ok_or_else(|| {
3293 self.program_error(
3294 module,
3295 RuntimeErrorKind::InvalidVerifiedProgram {
3296 module,
3297 instruction: Instruction::Halt,
3298 },
3299 )
3300 })
3301 })
3302 };
3303 self.finish_module_evaluation(module, result).map(Some)
3304 }
3305
3306 pub(crate) fn begin_module_evaluation(
3307 &mut self,
3308 module: ModuleId,
3309 ) -> Result<ModuleEvaluation, RuntimeError> {
3310 match self.registry.modules[module.get() as usize].state.clone() {
3311 ModuleState::Evaluating => return Ok(ModuleEvaluation::Cycle),
3312 ModuleState::Evaluated(result) => return Ok(ModuleEvaluation::Evaluated(result)),
3313 ModuleState::Unevaluated => {}
3314 }
3315 self.registry.modules[module.get() as usize].state = ModuleState::Evaluating;
3316
3317 let mut dependencies = Vec::new();
3318 for (edge_index, edge) in self
3319 .program_module(module)
3320 .edges
3321 .iter()
3322 .copied()
3323 .enumerate()
3324 {
3325 if !edge.kind.has_static() {
3326 continue;
3327 }
3328 match edge.target {
3329 EdgeTarget::Local(dependency) => dependencies.push(dependency),
3330 EdgeTarget::External
3331 if self
3332 .external_specifier(module, EdgeId::new(edge_index as u32))
3333 .is_some() => {}
3334 EdgeTarget::External => {
3335 let error = self.program_error(
3336 module,
3337 RuntimeErrorKind::ExternalModuleUnavailable {
3338 module,
3339 edge: EdgeId::new(edge_index as u32),
3340 },
3341 );
3342 self.settle_module_evaluation(module, Err(error.clone()));
3343 return Err(error);
3344 }
3345 }
3346 }
3347 Ok(ModuleEvaluation::Ready(dependencies))
3348 }
3349
3350 pub(crate) fn finish_module_evaluation(
3351 &mut self,
3352 module: ModuleId,
3353 result: Result<Execution, RuntimeError>,
3354 ) -> Result<Execution, RuntimeError> {
3355 if result.is_err() {
3356 self.frames.clear();
3357 self.live_registers = 0;
3358 }
3359 let stored = result.as_ref().map(|_| ()).map_err(Clone::clone);
3360 self.settle_module_evaluation(module, stored);
3361 result
3362 }
3363
3364 pub(crate) fn settle_module_evaluation(
3365 &mut self,
3366 module: ModuleId,
3367 result: Result<(), RuntimeError>,
3368 ) {
3369 match result {
3370 Ok(()) => {
3371 self.registry.modules[module.get() as usize].state = ModuleState::Evaluated(Ok(()));
3372 }
3373 Err(error) if matches!(error.kind, RuntimeErrorKind::UncaughtThrow { .. }) => {
3374 self.registry.modules[module.get() as usize].state =
3375 ModuleState::Evaluated(Err(error));
3376 }
3377 Err(_) => self.abort_module_evaluation(module),
3378 }
3379 }
3380
3381 pub(crate) fn abort_module_evaluation(&mut self, module: ModuleId) {
3382 if matches!(
3383 self.registry.modules[module.get() as usize].state,
3384 ModuleState::Evaluating
3385 ) {
3386 self.registry.modules[module.get() as usize].state = ModuleState::Unevaluated;
3387 }
3388 }
3389
3390 pub(crate) fn constant_text(&self, module: ModuleId, id: ConstantId) -> &EcmaString {
3391 match &self.module_code(module).constants()[id.get() as usize] {
3392 Constant::String(text) => text,
3393 _ => unreachable!("verified module names are strings"),
3394 }
3395 }
3396
3397 fn program_error(&self, module: ModuleId, kind: RuntimeErrorKind) -> RuntimeError {
3398 let code = self.module_code(module);
3399 let function = code.entry().get() as usize;
3400 let instruction = code.functions()[function]
3401 .code()
3402 .first()
3403 .copied()
3404 .unwrap_or(Instruction::Halt);
3405 RuntimeError {
3406 kind,
3407 function: FunctionId::new(function as u32),
3408 pc: Pc::new(0),
3409 source: RuntimeSource {
3410 function_name: None,
3411 instruction,
3412 },
3413 }
3414 }
3415
3416 fn run_loop(&mut self, stop_depth: usize) -> Result<Option<Execution>, RuntimeError> {
3417 if self.frames.len().saturating_add(self.native_depth) > self.limits.max_call_depth {
3418 return Err(self.error_here(RuntimeErrorKind::CallDepthExceeded {
3419 limit: self.limits.max_call_depth,
3420 }));
3421 }
3422 if self.live_registers > self.limits.max_total_registers {
3423 return Err(self.error_here(RuntimeErrorKind::RegisterLimitExceeded {
3424 limit: self.limits.max_total_registers,
3425 }));
3426 }
3427
3428 loop {
3429 let frame_index = self.frames.len() - 1;
3430 let (module_id, function_index, pc) = {
3431 let frame = &self.frames[frame_index];
3432 (frame.module, frame.function, frame.pc)
3433 };
3434 if let Err(kind) = self.consume_fuel(1) {
3435 return Err(self.error_at(kind, function_index, pc));
3436 }
3437 let instruction = self.module_code(module_id).functions()[function_index].code()[pc];
3438
3439 match instruction {
3440 Instruction::LoadConst { dst, constant } => {
3441 let value = self.load_constant(constant, function_index, pc)?;
3442 self.write_register(frame_index, dst.get(), value);
3443 self.frames[frame_index].pc = pc + 1;
3444 }
3445 Instruction::Move { dst, src } => {
3446 let value = self.read_register(frame_index, src.get());
3447 self.write_register(frame_index, dst.get(), value);
3448 self.frames[frame_index].pc = pc + 1;
3449 }
3450 Instruction::Unary { dst, op, operand } => {
3451 let value = self.read_register(frame_index, operand.get());
3452 match self.eval_unary(op, value) {
3453 Ok(result) => {
3454 self.write_register(frame_index, dst.get(), result);
3455 self.frames[frame_index].pc = pc + 1;
3456 }
3457 Err(failure) => self.resolve_failure(failure, pc)?,
3458 }
3459 }
3460 Instruction::Binary {
3461 dst,
3462 op,
3463 left,
3464 right,
3465 } => {
3466 let left = self.read_register(frame_index, left.get());
3467 let right = self.read_register(frame_index, right.get());
3468 match self.eval_binary(op, left, right) {
3469 Ok(result) => {
3470 self.write_register(frame_index, dst.get(), result);
3471 self.frames[frame_index].pc = pc + 1;
3472 }
3473 Err(failure) => self.resolve_failure(failure, pc)?,
3474 }
3475 }
3476 Instruction::CreateObject { dst } => {
3477 let value = self
3478 .allocate(HeapEntry::Object {
3479 properties: PropertyMap::default(),
3480 prototype: Some(self.intrinsics.object_prototype),
3481 boxed_primitive: None,
3482 extensible: true,
3483 })
3484 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3485 self.write_register(frame_index, dst.get(), value);
3486 self.frames[frame_index].pc = pc + 1;
3487 }
3488 Instruction::CreateArray { dst } => {
3489 let value = self
3490 .allocate(HeapEntry::Array {
3491 elements: Vec::new(),
3492 properties: PropertyMap::default(),
3493 prototype: Some(self.intrinsics.array_prototype),
3494 extensible: true,
3495 length_writable: true,
3496 })
3497 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3498 self.write_register(frame_index, dst.get(), value);
3499 self.frames[frame_index].pc = pc + 1;
3500 }
3501 Instruction::CreateCell { dst } => {
3502 let value = self
3503 .allocate(HeapEntry::Array {
3504 elements: vec![Value::UNINITIALIZED],
3505 properties: PropertyMap::default(),
3506 prototype: Some(self.intrinsics.array_prototype),
3507 extensible: true,
3508 length_writable: true,
3509 })
3510 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3511 self.write_register(frame_index, dst.get(), value);
3512 self.frames[frame_index].pc = pc + 1;
3513 }
3514 Instruction::CreateClosure {
3515 dst,
3516 function,
3517 captures,
3518 } => match self.read_captures(frame_index, captures.get(), function) {
3519 Ok(captures) => {
3520 let value = self
3521 .allocate(HeapEntry::Function {
3522 module: module_id,
3523 function,
3524 captures,
3525 properties: PropertyMap::default(),
3526 prototype: Some(self.intrinsics.function_prototype),
3527 extensible: true,
3528 })
3529 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3530 self.write_register(frame_index, dst.get(), value);
3531 self.frames[frame_index].pc = pc + 1;
3532 }
3533 Err(failure) => self.resolve_failure(failure, pc)?,
3534 },
3535 Instruction::GetProperty { dst, object, key } => {
3536 let object = self.read_register(frame_index, object.get());
3537 let key_value = self.read_register(frame_index, key.get());
3538 let key = match self.to_property_key(key_value) {
3539 Ok(key) => key,
3540 Err(failure) => {
3541 self.resolve_failure(failure, pc)?;
3542 continue;
3543 }
3544 };
3545 match self.resolve_get(object, &key) {
3546 Ok(GetOutcome::Value(value)) => {
3547 self.write_register(frame_index, dst.get(), value);
3548 self.frames[frame_index].pc = pc + 1;
3549 }
3550 Ok(GetOutcome::Text(text)) => {
3551 let value = self
3552 .allocate(HeapEntry::String(text))
3553 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3554 self.write_register(frame_index, dst.get(), value);
3555 self.frames[frame_index].pc = pc + 1;
3556 }
3557 Ok(GetOutcome::Getter(getter)) => {
3558 self.frames[frame_index].pc = pc + 1;
3559 self.execute_call(CallRequest {
3560 callee: getter,
3561 this_value: object,
3562 arguments: &[],
3563 destination: Some(dst.get()),
3564 call_pc: pc,
3565 constructed: None,
3566 new_target: Value::UNDEFINED,
3567 })?;
3568 }
3569 Err(failure) => self.resolve_failure(failure, pc)?,
3570 }
3571 }
3572 Instruction::SetProperty { object, key, value } => {
3573 let object = self.read_register(frame_index, object.get());
3574 let value = self.read_register(frame_index, value.get());
3575 let key_value = self.read_register(frame_index, key.get());
3576 let key = match self.to_property_key(key_value) {
3577 Ok(key) => key,
3578 Err(failure) => {
3579 self.resolve_failure(failure, pc)?;
3580 continue;
3581 }
3582 };
3583 match self.resolve_set(object, key, value) {
3584 Ok(SetOutcome::Done) => self.frames[frame_index].pc = pc + 1,
3585 Ok(SetOutcome::Setter(setter)) => {
3586 self.frames[frame_index].pc = pc + 1;
3587 self.execute_call(CallRequest {
3588 callee: setter,
3589 this_value: object,
3590 arguments: &[value],
3591 destination: None,
3592 call_pc: pc,
3593 constructed: None,
3594 new_target: Value::UNDEFINED,
3595 })?;
3596 }
3597 Err(failure) => self.resolve_failure(failure, pc)?,
3598 }
3599 }
3600 Instruction::DeleteProperty { dst, object, key } => {
3601 let object = self.read_register(frame_index, object.get());
3602 let key_value = self.read_register(frame_index, key.get());
3603 let key = match self.to_property_key(key_value) {
3604 Ok(key) => key,
3605 Err(failure) => {
3606 self.resolve_failure(failure, pc)?;
3607 continue;
3608 }
3609 };
3610 match self.delete_property(object, &key) {
3611 Ok(deleted) => {
3612 self.write_register(frame_index, dst.get(), Value::boolean(deleted));
3613 self.frames[frame_index].pc = pc + 1;
3614 }
3615 Err(failure) => self.resolve_failure(failure, pc)?,
3616 }
3617 }
3618 Instruction::DefineAccessor {
3619 object,
3620 key,
3621 accessor,
3622 kind,
3623 } => {
3624 let object = self.read_register(frame_index, object.get());
3625 let accessor = self.read_register(frame_index, accessor.get());
3626 let key_value = self.read_register(frame_index, key.get());
3627 let key = match self.to_property_key(key_value) {
3628 Ok(key) => key,
3629 Err(failure) => {
3630 self.resolve_failure(failure, pc)?;
3631 continue;
3632 }
3633 };
3634 match self.define_accessor(object, key, accessor, kind) {
3635 Ok(()) => self.frames[frame_index].pc = pc + 1,
3636 Err(failure) => self.resolve_failure(failure, pc)?,
3637 }
3638 }
3639 Instruction::Call {
3640 dst,
3641 callee,
3642 this_value,
3643 arguments,
3644 } => {
3645 let callee = self.read_register(frame_index, callee.get());
3646 let this_value = self.read_register(frame_index, this_value.get());
3647 match self.read_arguments(frame_index, arguments.get()) {
3648 Ok(arguments) => {
3649 self.frames[frame_index].pc = pc + 1;
3650 self.execute_call(CallRequest {
3651 callee,
3652 this_value,
3653 arguments: &arguments,
3654 destination: Some(dst.get()),
3655 call_pc: pc,
3656 constructed: None,
3657 new_target: Value::UNDEFINED,
3658 })?;
3659 }
3660 Err(failure) => self.resolve_failure(failure, pc)?,
3661 }
3662 }
3663 Instruction::Construct {
3664 dst,
3665 callee,
3666 arguments,
3667 } => {
3668 let callee = self.read_register(frame_index, callee.get());
3669 match self.read_arguments(frame_index, arguments.get()) {
3670 Ok(arguments) => {
3671 self.frames[frame_index].pc = pc + 1;
3672 self.execute_construct(callee, &arguments, dst.get(), pc)?;
3673 }
3674 Err(failure) => self.resolve_failure(failure, pc)?,
3675 }
3676 }
3677 Instruction::LoadGlobal { dst, name } => match self.load_global(module_id, name) {
3678 Ok(Some(value)) => {
3679 self.write_register(frame_index, dst.get(), value);
3680 self.frames[frame_index].pc = pc + 1;
3681 }
3682 Ok(None) => self.throw(
3683 Value::UNDEFINED,
3684 ThrowOrigin::ReferenceError {
3685 operation: "global is not defined",
3686 },
3687 pc,
3688 )?,
3689 Err(kind) => return Err(self.error_here_at(kind, pc)),
3690 },
3691 Instruction::StoreGlobal { name, value } => {
3692 let value = self.read_register(frame_index, value.get());
3693 match self.store_global(module_id, name, value) {
3694 Ok(()) => self.frames[frame_index].pc = pc + 1,
3695 Err(failure) => self.resolve_failure(failure, pc)?,
3696 }
3697 }
3698 Instruction::TypeOfGlobal { dst, name } => {
3699 let text = match self.load_global(module_id, name) {
3700 Ok(value) => value.map_or("undefined", |value| self.type_of(value)),
3701 Err(kind) => return Err(self.error_here_at(kind, pc)),
3702 };
3703 let value = self
3704 .allocate(HeapEntry::String(EcmaString::from_utf8(text)))
3705 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3706 self.write_register(frame_index, dst.get(), value);
3707 self.frames[frame_index].pc = pc + 1;
3708 }
3709 Instruction::LoadThis { dst } => {
3710 let value = self.frames[frame_index].this_value;
3711 self.write_register(frame_index, dst.get(), value);
3712 self.frames[frame_index].pc = pc + 1;
3713 }
3714 Instruction::LoadArguments { dst } => {
3715 let value = self.materialize_arguments(frame_index, function_index, pc)?;
3716 self.write_register(frame_index, dst.get(), value);
3717 self.frames[frame_index].pc = pc + 1;
3718 }
3719 Instruction::LoadNewTarget { dst } => {
3720 let value = self.frames[frame_index].new_target;
3721 self.write_register(frame_index, dst.get(), value);
3722 self.frames[frame_index].pc = pc + 1;
3723 }
3724 Instruction::ArrayPush { array, value } => {
3725 let array = self.read_register(frame_index, array.get());
3726 let value = self.read_register(frame_index, value.get());
3727 match self.array_push(array, value) {
3728 Ok(()) => self.frames[frame_index].pc = pc + 1,
3729 Err(failure) => self.resolve_failure(failure, pc)?,
3730 }
3731 }
3732 Instruction::ArrayExtend { array, iterable } => {
3733 let array = self.read_register(frame_index, array.get());
3734 let iterable = self.read_register(frame_index, iterable.get());
3735 match self.array_extend(array, iterable) {
3736 Ok(()) => self.frames[frame_index].pc = pc + 1,
3737 Err(failure) => self.resolve_failure(failure, pc)?,
3738 }
3739 }
3740 Instruction::ObjectSpread { target, source } => {
3741 let target = self.read_register(frame_index, target.get());
3742 let source = self.read_register(frame_index, source.get());
3743 match self.object_spread(target, source) {
3744 Ok(()) => self.frames[frame_index].pc = pc + 1,
3745 Err(failure) => self.resolve_failure(failure, pc)?,
3746 }
3747 }
3748 Instruction::SetPrototype { object, prototype } => {
3749 let object = self.read_register(frame_index, object.get());
3750 let prototype = self.read_register(frame_index, prototype.get());
3751 match self.set_prototype(object, prototype) {
3752 Ok(()) => self.frames[frame_index].pc = pc + 1,
3753 Err(failure) => self.resolve_failure(failure, pc)?,
3754 }
3755 }
3756 Instruction::CreatePrivateName { dst, description } => {
3757 let description = self.constant_string(description).clone();
3758 let value = self
3759 .allocate(HeapEntry::PrivateName { description })
3760 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3761 self.write_register(frame_index, dst.get(), value);
3762 self.frames[frame_index].pc = pc + 1;
3763 }
3764 Instruction::CreateRegExp {
3765 dst,
3766 pattern,
3767 flags,
3768 } => {
3769 let pattern = self.constant_string(pattern).clone();
3770 let flags = self.constant_string(flags).clone();
3771 let value = self
3772 .allocate(HeapEntry::RegExp {
3773 pattern,
3774 flags,
3775 properties: PropertyMap::default(),
3776 prototype: Some(self.intrinsics.regexp_prototype()),
3777 extensible: true,
3778 })
3779 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3780 self.write_register(frame_index, dst.get(), value);
3781 self.frames[frame_index].pc = pc + 1;
3782 }
3783 Instruction::GetIterator { dst, src, kind } => {
3784 let src = self.read_register(frame_index, src.get());
3785 match self.create_iterator(src, kind) {
3786 Ok(value) => {
3787 self.write_register(frame_index, dst.get(), value);
3788 self.frames[frame_index].pc = pc + 1;
3789 }
3790 Err(failure) => self.resolve_failure(failure, pc)?,
3791 }
3792 }
3793 Instruction::IteratorNext {
3794 done,
3795 value,
3796 iterator,
3797 } => {
3798 let iterator = self.read_register(frame_index, iterator.get());
3799 match self.iterator_next(iterator) {
3800 Ok((is_done, produced)) => {
3801 self.write_register(frame_index, done.get(), Value::boolean(is_done));
3802 self.write_register(frame_index, value.get(), produced);
3803 self.frames[frame_index].pc = pc + 1;
3804 }
3805 Err(failure) => self.resolve_failure(failure, pc)?,
3806 }
3807 }
3808 Instruction::Jump { target } => {
3809 self.frames[frame_index].pc = target.get() as usize;
3810 }
3811 Instruction::JumpIfTrue { condition, target } => {
3812 let condition = self.read_register(frame_index, condition.get());
3813 self.frames[frame_index].pc = if self.truthy(condition) {
3814 target.get() as usize
3815 } else {
3816 pc + 1
3817 };
3818 }
3819 Instruction::JumpIfFalse { condition, target } => {
3820 let condition = self.read_register(frame_index, condition.get());
3821 self.frames[frame_index].pc = if self.truthy(condition) {
3822 pc + 1
3823 } else {
3824 target.get() as usize
3825 };
3826 }
3827 Instruction::Return { value } => {
3828 let value = self.read_register(frame_index, value.get());
3829 if let Some(execution) = self.complete_frame(value) {
3830 return Ok(Some(execution));
3831 }
3832 if self.frames.len() == stop_depth {
3833 return Ok(None);
3834 }
3835 }
3836 Instruction::Throw { value } => {
3837 let value = self.read_register(frame_index, value.get());
3838 self.throw(value, ThrowOrigin::Bytecode, pc)?;
3839 }
3840 Instruction::Suspend { src, .. }
3841 if self
3842 .async_boundaries
3843 .last()
3844 .is_some_and(|boundary| *boundary == frame_index) =>
3845 {
3846 let awaited = self.read_register(frame_index, src.get());
3847 let frame = self.frames.pop().expect("async activation is executing");
3848 self.pending_async_suspend = Some((
3849 awaited,
3850 SuspendedActivation {
3851 target: RuntimeFunction {
3852 module: frame.module,
3853 function: FunctionId::new(frame.function as u32),
3854 },
3855 registers: frame.registers,
3856 this_value: frame.this_value,
3857 new_target: frame.new_target,
3858 args: frame.args,
3859 arguments_object: frame.arguments_object,
3860 resume_token: pc as u32 + 1,
3861 },
3862 ));
3863 return Ok(None);
3864 }
3865 Instruction::Suspend { src, .. }
3866 if self
3867 .generator_boundaries
3868 .last()
3869 .is_some_and(|boundary| *boundary == frame_index) =>
3870 {
3871 let value = self.read_register(frame_index, src.get());
3872 let frame = self
3873 .frames
3874 .pop()
3875 .expect("generator activation is executing");
3876 self.pending_generator_resume = Some(GeneratorResume::Yield {
3877 value,
3878 activation: SuspendedActivation {
3879 target: RuntimeFunction {
3880 module: frame.module,
3881 function: FunctionId::new(frame.function as u32),
3882 },
3883 registers: frame.registers,
3884 this_value: frame.this_value,
3885 new_target: frame.new_target,
3886 args: frame.args,
3887 arguments_object: frame.arguments_object,
3888 resume_token: pc as u32 + 1,
3889 },
3890 });
3891 return Ok(None);
3892 }
3893 Instruction::Suspend { .. } => {
3894 self.throw_type("suspend outside an engine-owned event loop", pc)?;
3895 }
3896 Instruction::Import { dst, specifier } => {
3897 match self.import_namespace(module_id, specifier) {
3898 Ok(namespace) => {
3899 self.write_register(frame_index, dst.get(), namespace);
3900 self.frames[frame_index].pc = pc + 1;
3901 }
3902 Err(failure) => self.resolve_failure(failure, pc)?,
3903 }
3904 }
3905 Instruction::Export { .. } => {
3906 return Err(self.error_here_at(
3907 RuntimeErrorKind::InvalidVerifiedProgram {
3908 module: module_id,
3909 instruction,
3910 },
3911 pc,
3912 ));
3913 }
3914 Instruction::Halt => {
3915 if let Some(execution) = self.complete_frame(Value::UNDEFINED) {
3916 return Ok(Some(execution));
3917 }
3918 if self.frames.len() == stop_depth {
3919 return Ok(None);
3920 }
3921 }
3922 }
3923 }
3924 }
3925
3926 fn read_register(&self, frame: usize, register: u32) -> Value {
3927 self.frames[frame].registers[register as usize]
3928 }
3929
3930 fn write_register(&mut self, frame: usize, register: u32, value: Value) {
3931 self.frames[frame].registers[register as usize] = value;
3932 }
3933
3934 fn constant_string(&self, id: ConstantId) -> &EcmaString {
3935 self.constant_text(self.active_module_id(), id)
3936 }
3937
3938 fn load_constant(
3939 &mut self,
3940 id: ConstantId,
3941 function: usize,
3942 pc: usize,
3943 ) -> Result<Value, RuntimeError> {
3944 self.load_constant_value(self.active_module_id(), id)
3945 .map_err(|kind| self.error_at(kind, function, pc))
3946 }
3947
3948 fn allocate(&mut self, entry: HeapEntry) -> Result<Value, RuntimeErrorKind> {
3949 let bytes = entry.initial_bytes();
3950 self.ensure_allocation_capacity(1, bytes)?;
3951 self.heap_bytes += bytes;
3952 let slot = self.heap.len() as u32 + 1;
3953 self.heap.push(entry);
3954 let id = SlotId::from_parts(RUNTIME_HEAP_SEGMENT, slot)
3955 .expect("runtime segment and one-based slot are nonzero");
3956 Ok(Value::heap_ref(id))
3957 }
3958
3959 fn ensure_allocation_capacity(
3960 &self,
3961 additional_slots: usize,
3962 additional_bytes: usize,
3963 ) -> Result<(), RuntimeErrorKind> {
3964 let used_slots = self.heap.len().saturating_sub(self.intrinsic_slots);
3965 let slots_fit_limit = used_slots
3966 .checked_add(additional_slots)
3967 .is_some_and(|total| total <= self.limits.max_heap_slots);
3968 let slots_fit_value = self
3969 .heap
3970 .len()
3971 .checked_add(additional_slots)
3972 .is_some_and(|total| total <= u32::MAX as usize);
3973 if !slots_fit_limit || !slots_fit_value {
3974 return Err(RuntimeErrorKind::HeapSlotLimitExceeded {
3975 limit: self.limits.max_heap_slots,
3976 });
3977 }
3978 let bytes_fit = self
3979 .heap_bytes
3980 .checked_add(additional_bytes)
3981 .is_some_and(|total| total <= self.limits.max_heap_bytes);
3982 if !bytes_fit {
3983 return Err(RuntimeErrorKind::HeapByteLimitExceeded {
3984 limit: self.limits.max_heap_bytes,
3985 });
3986 }
3987 Ok(())
3988 }
3989
3990 fn ensure_object_property_capacity(
3991 &self,
3992 property_bytes: usize,
3993 ) -> Result<(), RuntimeErrorKind> {
3994 let bytes =
3995 property_bytes
3996 .checked_add(1)
3997 .ok_or(RuntimeErrorKind::HeapByteLimitExceeded {
3998 limit: self.limits.max_heap_bytes,
3999 })?;
4000 self.ensure_allocation_capacity(1, bytes)
4001 }
4002 fn charge_heap(&mut self, bytes: usize) -> Result<(), RuntimeErrorKind> {
4003 self.ensure_allocation_capacity(0, bytes)?;
4004 self.heap_bytes += bytes;
4005 Ok(())
4006 }
4007
4008 fn runtime_slot(&self, value: Value) -> Result<Option<usize>, RuntimeErrorKind> {
4009 let Some(decoded) = value.decode() else {
4010 return Err(RuntimeErrorKind::InvalidValue { value });
4011 };
4012 let Decoded::HeapRef(id) = decoded else {
4013 return Ok(None);
4014 };
4015 if id.segment() != RUNTIME_HEAP_SEGMENT {
4016 return Err(RuntimeErrorKind::InvalidValue { value });
4017 }
4018 let index = id.slot() as usize - 1;
4019 if index >= self.heap.len() {
4020 return Err(RuntimeErrorKind::InvalidRuntimeHeapReference { slot: id.slot() });
4021 }
4022 Ok(Some(index))
4023 }
4024
4025 fn active_module_id(&self) -> ModuleId {
4026 self.frames
4027 .last()
4028 .map_or(ModuleId::new(0), |frame| frame.module)
4029 }
4030
4031 pub(crate) fn load_global(
4032 &self,
4033 module: ModuleId,
4034 name: ConstantId,
4035 ) -> Result<Option<Value>, RuntimeErrorKind> {
4036 if let Some(cell) = self
4037 .registry
4038 .modules
4039 .get(module.get() as usize)
4040 .and_then(|instance| instance.constant_cells.get(name.get() as usize))
4041 .copied()
4042 .flatten()
4043 {
4044 let value = self.registry.cells[cell.0].value;
4045 if value.is_uninitialized() {
4046 let binding = self.registry.modules[module.get() as usize]
4047 .binding_cells
4048 .iter()
4049 .position(|candidate| *candidate == Some(cell))
4050 .map(|index| BindingId::new(index as u32))
4051 .expect("linked cell belongs to a binding");
4052 return Err(RuntimeErrorKind::TemporalDeadZone { module, binding });
4053 }
4054 return Ok(Some(value));
4055 }
4056 Ok(self.resolve_global_binding(self.constant_text(module, name)))
4057 }
4058
4059 pub(crate) fn store_global(
4060 &mut self,
4061 module: ModuleId,
4062 name: ConstantId,
4063 value: Value,
4064 ) -> Result<(), EvalFailure> {
4065 let cell = self
4066 .registry
4067 .modules
4068 .get(module.get() as usize)
4069 .and_then(|instance| instance.constant_cells.get(name.get() as usize))
4070 .copied()
4071 .flatten();
4072 if let Some(cell) = cell {
4073 let binding = self.registry.modules[module.get() as usize]
4074 .binding_cells
4075 .iter()
4076 .position(|candidate| *candidate == Some(cell))
4077 .expect("mapped module cell belongs to a binding");
4078 if matches!(
4079 self.program_module(module).bindings[binding].kind,
4080 BindingKind::Imported { .. } | BindingKind::Namespace { .. }
4081 ) {
4082 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4083 operation: "assign to immutable module binding",
4084 }));
4085 }
4086 self.registry.cells[cell.0].value = value;
4087 } else {
4088 let name = self.constant_text(module, name).to_owned();
4089 if let Some(global_this) = self.intrinsics.global("globalThis") {
4090 let key = PropertyKey::Named(name.clone());
4091 if matches!(
4092 self.own_descriptor(global_this, &key)?,
4093 Some(
4094 Property::Data {
4095 writable: false,
4096 ..
4097 } | Property::Accessor { setter: None, .. }
4098 )
4099 ) {
4100 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4101 operation: "assign to non-writable global property",
4102 }));
4103 }
4104 }
4105 self.globals.insert(name, value);
4106 }
4107 Ok(())
4108 }
4109
4110 fn resolve_global_binding(&self, name: &EcmaString) -> Option<Value> {
4112 self.globals.get(name).copied().or_else(|| {
4113 self.intrinsics
4114 .globals
4115 .iter()
4116 .find_map(|(candidate, value)| (candidate == name).then_some(*value))
4117 })
4118 }
4119
4120 fn callee_kind(&self, callee: Value) -> Result<CalleeKind, RuntimeErrorKind> {
4122 match self.runtime_slot(callee)? {
4123 Some(index) => match &self.heap[index] {
4124 HeapEntry::Function {
4125 module,
4126 function,
4127 captures,
4128 ..
4129 } => Ok(CalleeKind::Runtime {
4130 target: RuntimeFunction {
4131 module: *module,
4132 function: *function,
4133 },
4134 captures: captures.clone(),
4135 }),
4136 HeapEntry::NativeFunction { callable, .. } => match callable {
4137 NativeCallable::Builtin(id) => Ok(CalleeKind::Builtin { id: *id }),
4138 NativeCallable::Bound(_) => Ok(CalleeKind::Bound),
4139 },
4140 _ => Ok(CalleeKind::NotCallable),
4141 },
4142 None => Ok(CalleeKind::NotCallable),
4143 }
4144 }
4145
4146 pub(crate) fn flatten_bound(
4147 &self,
4148 callee: Value,
4149 this_value: Value,
4150 arguments: &[Value],
4151 ) -> Result<BoundCall, RuntimeErrorKind> {
4152 let mut target = callee;
4153 let mut receiver = this_value;
4154 let mut segments = Vec::new();
4155 let mut total = arguments.len();
4156 while let Some(index) = self.runtime_slot(target)? {
4157 let HeapEntry::NativeFunction {
4158 callable: NativeCallable::Bound(bound),
4159 ..
4160 } = &self.heap[index]
4161 else {
4162 break;
4163 };
4164 total = total.checked_add(bound.arguments.len()).ok_or(
4165 RuntimeErrorKind::ArgumentLimitExceeded {
4166 limit: self.limits.max_argument_count,
4167 requested: u32::MAX,
4168 },
4169 )?;
4170 if total > self.limits.max_argument_count as usize {
4171 return Err(RuntimeErrorKind::ArgumentLimitExceeded {
4172 limit: self.limits.max_argument_count,
4173 requested: u32::try_from(total).unwrap_or(u32::MAX),
4174 });
4175 }
4176 segments.push(bound.arguments.as_slice());
4177 receiver = bound.this_value;
4178 target = bound.target;
4179 }
4180 let mut flattened = Vec::with_capacity(total);
4181 for segment in segments.iter().rev() {
4182 flattened.extend_from_slice(segment);
4183 }
4184 flattened.extend_from_slice(arguments);
4185 Ok(BoundCall {
4186 target,
4187 this_value: receiver,
4188 arguments: flattened,
4189 })
4190 }
4191
4192 fn bound_target(&self, mut value: Value) -> Result<Value, RuntimeErrorKind> {
4193 loop {
4194 let Some(index) = self.runtime_slot(value)? else {
4195 return Ok(value);
4196 };
4197 let HeapEntry::NativeFunction {
4198 callable: NativeCallable::Bound(bound),
4199 ..
4200 } = &self.heap[index]
4201 else {
4202 return Ok(value);
4203 };
4204 value = bound.target;
4205 }
4206 }
4207
4208 pub(crate) fn load_constant_value(
4211 &mut self,
4212 module: ModuleId,
4213 id: ConstantId,
4214 ) -> Result<Value, RuntimeErrorKind> {
4215 match &self.module_code(module).constants()[id.get() as usize] {
4216 Constant::String(text) => self.allocate(HeapEntry::String(text.clone())),
4217 Constant::BigInt(value) => self.allocate(HeapEntry::BigInt(value.as_str().to_owned())),
4218 constant => Ok(constant_value(constant).expect("non-heap constant")),
4219 }
4220 }
4221
4222 fn read_arguments(&self, frame: usize, register: u32) -> Result<Vec<Value>, EvalFailure> {
4225 let value = self.read_register(frame, register);
4226 self.arguments_from_array(value)
4227 }
4228
4229 fn arguments_from_array(&self, arguments: Value) -> Result<Vec<Value>, EvalFailure> {
4233 match self.runtime_slot(arguments).map_err(EvalFailure::Runtime)? {
4234 Some(index) => match &self.heap[index] {
4235 HeapEntry::Array { elements, .. } => {
4236 if elements.len() as u64 > u64::from(self.limits.max_argument_count) {
4237 return Err(EvalFailure::Runtime(
4238 RuntimeErrorKind::ArgumentLimitExceeded {
4239 limit: self.limits.max_argument_count,
4240 requested: u32::try_from(elements.len()).unwrap_or(u32::MAX),
4241 },
4242 ));
4243 }
4244 Ok(elements
4245 .iter()
4246 .map(|value| {
4247 if *value == Value::HOLE {
4248 Value::UNDEFINED
4249 } else {
4250 *value
4251 }
4252 })
4253 .collect())
4254 }
4255 _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4256 operation: "call arguments are not an array",
4257 })),
4258 },
4259 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4260 operation: "call arguments are not an array",
4261 })),
4262 }
4263 }
4264
4265 fn read_captures(
4268 &self,
4269 frame: usize,
4270 register: u32,
4271 function: FunctionId,
4272 ) -> Result<Vec<Value>, EvalFailure> {
4273 let value = self.read_register(frame, register);
4274 self.captures_from_array(self.active_module_id(), value, function)
4275 }
4276
4277 pub(crate) fn captures_from_array(
4281 &self,
4282 module: ModuleId,
4283 captures: Value,
4284 function: FunctionId,
4285 ) -> Result<Vec<Value>, EvalFailure> {
4286 let expected =
4287 self.module_code(module).functions()[function.get() as usize].capture_count() as usize;
4288 match self.runtime_slot(captures).map_err(EvalFailure::Runtime)? {
4289 Some(index) => match &self.heap[index] {
4290 HeapEntry::Array { elements, .. } => {
4291 if elements.len() != expected {
4292 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4293 operation: "closure capture array arity",
4294 }));
4295 }
4296 Ok(elements
4297 .iter()
4298 .map(|value| {
4299 if *value == Value::HOLE {
4300 Value::UNDEFINED
4301 } else {
4302 *value
4303 }
4304 })
4305 .collect())
4306 }
4307 _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4308 operation: "closure captures are not an array",
4309 })),
4310 },
4311 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4312 operation: "closure captures are not an array",
4313 })),
4314 }
4315 }
4316
4317 pub(crate) fn materialize_arguments(
4318 &mut self,
4319 frame: usize,
4320 function: usize,
4321 pc: usize,
4322 ) -> Result<Value, RuntimeError> {
4323 if let Some(existing) = self.frames[frame].arguments_object {
4324 return Ok(existing);
4325 }
4326 let args = self.frames[frame].args.clone();
4327 let value = self
4328 .allocate(HeapEntry::Array {
4329 elements: args,
4330 properties: PropertyMap::default(),
4331 prototype: Some(self.intrinsics.array_prototype),
4332 extensible: true,
4333 length_writable: true,
4334 })
4335 .map_err(|kind| self.error_at(kind, function, pc))?;
4336 self.frames[frame].arguments_object = Some(value);
4337 Ok(value)
4338 }
4339
4340 fn push_frame(
4341 &mut self,
4342 target: RuntimeFunction,
4343 captures: &[Value],
4344 this_value: Value,
4345 new_target: Value,
4346 arguments: &[Value],
4347 return_to: Option<ReturnTo>,
4348 ) -> Result<(), RuntimeError> {
4349 let function_index = target.function.get() as usize;
4350 let metadata = &self.module_code(target.module).functions()[function_index];
4351 let limit_error = |kind| match (self.frames.last(), return_to) {
4352 (Some(caller), Some(return_to)) => {
4353 self.error_at_in_module(kind, caller.module, caller.function, return_to.call_pc)
4354 }
4355 (_, None) => self.error_at_in_module(kind, target.module, function_index, 0),
4356 (None, Some(_)) => unreachable!("a returning frame has a caller"),
4357 };
4358 if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4359 return Err(limit_error(RuntimeErrorKind::CallDepthExceeded {
4360 limit: self.limits.max_call_depth,
4361 }));
4362 }
4363 let next_registers = metadata.register_count() as usize;
4364 if self.live_registers.saturating_add(next_registers) > self.limits.max_total_registers {
4365 return Err(limit_error(RuntimeErrorKind::RegisterLimitExceeded {
4366 limit: self.limits.max_total_registers,
4367 }));
4368 }
4369 let frame = Frame::new(
4370 target, metadata, captures, this_value, new_target, arguments, return_to,
4371 );
4372 self.live_registers += next_registers;
4373 self.frames.push(frame);
4374 Ok(())
4375 }
4376
4377 pub(crate) fn consume_fuel(&mut self, amount: u64) -> Result<(), RuntimeErrorKind> {
4378 if self.fuel < amount {
4379 self.fuel = 0;
4380 return Err(RuntimeErrorKind::FuelExhausted {
4381 limit: self.limits.fuel,
4382 });
4383 }
4384 self.fuel -= amount;
4385 Ok(())
4386 }
4387
4388 pub(crate) fn reserve_native_activation(
4389 &mut self,
4390 register_count: usize,
4391 ) -> Result<(), RuntimeErrorKind> {
4392 if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4393 return Err(RuntimeErrorKind::CallDepthExceeded {
4394 limit: self.limits.max_call_depth,
4395 });
4396 }
4397 if self.live_registers.saturating_add(register_count) > self.limits.max_total_registers {
4398 return Err(RuntimeErrorKind::RegisterLimitExceeded {
4399 limit: self.limits.max_total_registers,
4400 });
4401 }
4402 self.native_depth += 1;
4403 self.live_registers += register_count;
4404 Ok(())
4405 }
4406
4407 pub(crate) fn release_native_activation(&mut self, register_count: usize) {
4408 self.native_depth -= 1;
4409 self.live_registers -= register_count;
4410 }
4411
4412 pub(crate) fn reserve_suspended_activation_registers(
4413 &mut self,
4414 register_count: usize,
4415 ) -> Result<(), RuntimeErrorKind> {
4416 if self.live_registers.saturating_add(register_count) > self.limits.max_total_registers {
4417 return Err(RuntimeErrorKind::RegisterLimitExceeded {
4418 limit: self.limits.max_total_registers,
4419 });
4420 }
4421 self.live_registers += register_count;
4422 Ok(())
4423 }
4424
4425 pub(crate) fn release_suspended_activation_registers(&mut self, register_count: usize) {
4426 self.live_registers -= register_count;
4427 }
4428
4429 pub(crate) fn enter_native_generator(&mut self) -> Result<(), RuntimeErrorKind> {
4430 if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4431 return Err(RuntimeErrorKind::CallDepthExceeded {
4432 limit: self.limits.max_call_depth,
4433 });
4434 }
4435 self.native_depth += 1;
4436 Ok(())
4437 }
4438
4439 pub(crate) fn leave_native_generator(&mut self) {
4440 self.native_depth -= 1;
4441 }
4442
4443 fn execute_call(&mut self, request: CallRequest<'_>) -> Result<(), RuntimeError> {
4444 let CallRequest {
4445 callee,
4446 this_value,
4447 arguments,
4448 destination,
4449 call_pc,
4450 constructed,
4451 new_target,
4452 } = request;
4453 let mut callee = callee;
4454 let mut this_value = this_value;
4455 let mut arguments = Cow::Borrowed(arguments);
4456 loop {
4457 match self.callee_kind(callee) {
4458 Ok(CalleeKind::Runtime { target, captures }) => {
4459 let flags = self.module_code(target.module).functions()
4460 [target.function.get() as usize]
4461 .flags();
4462 if flags.is_generator && !flags.is_async {
4463 let generator = self
4464 .create_generator(GeneratorStart {
4465 target,
4466 captures,
4467 this_value,
4468 new_target,
4469 args: arguments.as_ref().to_vec(),
4470 })
4471 .map_err(|kind| self.error_here_at(kind, call_pc))?;
4472 if let Some(register) = destination {
4473 self.write_register(self.frames.len() - 1, register, generator);
4474 }
4475 return Ok(());
4476 }
4477 if flags.is_async && !flags.is_generator {
4478 return match self.start_async_call(
4479 target,
4480 &captures,
4481 this_value,
4482 new_target,
4483 arguments.as_ref(),
4484 ) {
4485 Ok(promise) => {
4486 if let Some(register) = destination {
4487 self.write_register(self.frames.len() - 1, register, promise);
4488 }
4489 Ok(())
4490 }
4491 Err(failure) => self.resolve_failure(failure, call_pc),
4492 };
4493 }
4494 return self.push_frame(
4495 target,
4496 &captures,
4497 this_value,
4498 new_target,
4499 arguments.as_ref(),
4500 Some(ReturnTo {
4501 destination: destination.map(|register| register as usize),
4502 call_pc,
4503 constructed,
4504 }),
4505 );
4506 }
4507 Ok(CalleeKind::Builtin { id }) => {
4508 match self.call_builtin(id, this_value, arguments.as_ref(), false) {
4509 Ok(intrinsics::BuiltinOutcome::Value(value)) => {
4510 if let Some(register) = destination {
4511 self.write_register(self.frames.len() - 1, register, value);
4512 }
4513 return Ok(());
4514 }
4515 Ok(intrinsics::BuiltinOutcome::Call {
4516 callee: next,
4517 this_value: next_this,
4518 arguments: next_arguments,
4519 }) => {
4520 callee = next;
4521 this_value = next_this;
4522 arguments = Cow::Owned(next_arguments);
4523 }
4524 Ok(intrinsics::BuiltinOutcome::GeneratorNext {
4525 generator,
4526 resume_value,
4527 }) => match self.resume_generator(generator, resume_value) {
4528 Ok(value) => {
4529 if let Some(register) = destination {
4530 self.write_register(self.frames.len() - 1, register, value);
4531 }
4532 return Ok(());
4533 }
4534 Err(failure) => return self.resolve_failure(failure, call_pc),
4535 },
4536 Ok(intrinsics::BuiltinOutcome::ConstructCall { .. }) => {
4537 return self.throw_type("call", call_pc);
4538 }
4539 Err(failure) => return self.resolve_failure(failure, call_pc),
4540 }
4541 }
4542 Ok(CalleeKind::Bound) => {
4543 let bound = self
4544 .flatten_bound(callee, this_value, arguments.as_ref())
4545 .map_err(|kind| self.error_here_at(kind, call_pc))?;
4546 callee = bound.target;
4547 if constructed.is_none() {
4548 this_value = bound.this_value;
4549 }
4550 arguments = Cow::Owned(bound.arguments);
4551 }
4552 Ok(CalleeKind::NotCallable) => return self.throw_type("call", call_pc),
4553 Err(kind) => return Err(self.error_here_at(kind, call_pc)),
4554 }
4555 }
4556 }
4557
4558 fn execute_construct(
4559 &mut self,
4560 callee: Value,
4561 arguments: &[Value],
4562 destination: u32,
4563 call_pc: usize,
4564 ) -> Result<(), RuntimeError> {
4565 let mut callee = callee;
4566 let mut arguments = Cow::Borrowed(arguments);
4567 if matches!(self.callee_kind(callee), Ok(CalleeKind::Bound)) {
4568 let bound = self
4569 .flatten_bound(callee, Value::UNDEFINED, arguments.as_ref())
4570 .map_err(|kind| self.error_here_at(kind, call_pc))?;
4571 callee = bound.target;
4572 arguments = Cow::Owned(bound.arguments);
4573 }
4574 let index = match self.runtime_slot(callee) {
4575 Ok(Some(index)) => index,
4576 Ok(None) => return self.throw_type("construct", call_pc),
4577 Err(kind) => return Err(self.error_here_at(kind, call_pc)),
4578 };
4579 let builtin = match &self.heap[index] {
4580 HeapEntry::NativeFunction {
4581 callable: NativeCallable::Builtin(id),
4582 ..
4583 } => Some(*id),
4584 _ => None,
4585 };
4586 if let Some(id) = builtin {
4587 return match self.call_builtin(id, Value::UNDEFINED, arguments.as_ref(), true) {
4588 Ok(intrinsics::BuiltinOutcome::Value(value)) => {
4589 self.write_register(self.frames.len() - 1, destination, value);
4590 Ok(())
4591 }
4592 Ok(
4593 intrinsics::BuiltinOutcome::Call { .. }
4594 | intrinsics::BuiltinOutcome::GeneratorNext { .. },
4595 ) => self.throw_type("construct", call_pc),
4596 Ok(intrinsics::BuiltinOutcome::ConstructCall {
4597 callee: continuation,
4598 this_value,
4599 arguments: continuation_arguments,
4600 prototype,
4601 }) => {
4602 let object = self
4603 .allocate_constructed_receiver_with(prototype)
4604 .map_err(|kind| self.error_here_at(kind, call_pc))?;
4605 self.execute_call(CallRequest {
4606 callee: continuation,
4607 this_value,
4608 arguments: &continuation_arguments,
4609 destination: Some(destination),
4610 call_pc,
4611 constructed: Some(object),
4612 new_target: callee,
4613 })
4614 }
4615 Err(failure) => self.resolve_failure(failure, call_pc),
4616 };
4617 }
4618 if !matches!(
4619 self.heap[index],
4620 HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. }
4621 ) {
4622 return self.throw_type("construct", call_pc);
4623 }
4624 if let HeapEntry::Function {
4625 module, function, ..
4626 } = self.heap[index]
4627 {
4628 if self.module_code(module).functions()[function.get() as usize]
4629 .flags()
4630 .is_async
4631 {
4632 return self.throw_type("construct", call_pc);
4633 }
4634 }
4635 let object = self
4636 .allocate_constructed_receiver(callee)
4637 .map_err(|kind| self.error_here_at(kind, call_pc))?;
4638 self.execute_call(CallRequest {
4639 callee,
4640 this_value: object,
4641 arguments: arguments.as_ref(),
4642 destination: Some(destination),
4643 call_pc,
4644 constructed: Some(object),
4645 new_target: callee,
4646 })
4647 }
4648
4649 fn constructed_prototype(&self, callee: Value) -> Result<Value, RuntimeErrorKind> {
4650 let index = self
4651 .runtime_slot(callee)?
4652 .ok_or(RuntimeErrorKind::InvalidValue { value: callee })?;
4653 Ok(match self.own_data_property(index, "prototype") {
4654 Some(value) if self.is_object(value) => value,
4655 _ => self.intrinsics.object_prototype,
4656 })
4657 }
4658
4659 fn allocate_constructed_receiver(&mut self, callee: Value) -> Result<Value, RuntimeErrorKind> {
4660 let prototype = self.constructed_prototype(callee)?;
4661 self.allocate_constructed_receiver_with(prototype)
4662 }
4663
4664 fn allocate_constructed_receiver_with(
4665 &mut self,
4666 prototype: Value,
4667 ) -> Result<Value, RuntimeErrorKind> {
4668 self.allocate(HeapEntry::Object {
4669 properties: PropertyMap::default(),
4670 prototype: Some(prototype),
4671 boxed_primitive: None,
4672 extensible: true,
4673 })
4674 }
4675
4676 pub(crate) fn array_elements(&self, value: Value) -> Result<Option<Vec<Value>>, EvalFailure> {
4677 let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4678 return Ok(None);
4679 };
4680 match &self.heap[index] {
4681 HeapEntry::Array { elements, .. } => Ok(Some(elements.clone())),
4682 _ => Ok(None),
4683 }
4684 }
4685
4686 pub(crate) fn array_length(&self, value: Value) -> Result<usize, EvalFailure> {
4687 self.array_elements(value)?
4688 .map(|elements| elements.len())
4689 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
4690 operation: "array method called on incompatible receiver",
4691 }))
4692 }
4693
4694 pub(crate) fn replace_array_elements(
4695 &mut self,
4696 value: Value,
4697 elements: Vec<Value>,
4698 ) -> Result<(), EvalFailure> {
4699 let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4700 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4701 operation: "array method called on incompatible receiver",
4702 }));
4703 };
4704 let HeapEntry::Array {
4705 elements: current, ..
4706 } = &mut self.heap[index]
4707 else {
4708 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4709 operation: "array method called on incompatible receiver",
4710 }));
4711 };
4712 *current = elements;
4713 Ok(())
4714 }
4715
4716 pub(crate) fn string_value(&self, value: Value) -> Option<EcmaString> {
4717 let index = self.runtime_slot(value).ok().flatten()?;
4718 match &self.heap[index] {
4719 HeapEntry::String(text) => Some(text.clone()),
4720 _ => None,
4721 }
4722 }
4723
4724 pub(crate) fn get_named_property(
4725 &mut self,
4726 object: Value,
4727 name: &str,
4728 ) -> Result<Value, EvalFailure> {
4729 self.get_property_ascii(object, name)
4730 }
4731
4732 fn get_property_ascii(&mut self, object: Value, name: &str) -> Result<Value, EvalFailure> {
4733 debug_assert!(name.is_ascii());
4734 match self.resolve_get_ascii(object, name)? {
4735 GetOutcome::Value(value) => Ok(value),
4736 GetOutcome::Text(text) => self
4737 .allocate(HeapEntry::String(text))
4738 .map_err(EvalFailure::Runtime),
4739 GetOutcome::Getter(getter) => self.call_value(getter, object, &[]),
4740 }
4741 }
4742
4743 pub(crate) fn get_property_key(
4744 &mut self,
4745 object: Value,
4746 key: &PropertyKey,
4747 ) -> Result<Value, EvalFailure> {
4748 match self.resolve_get(object, key)? {
4749 GetOutcome::Value(value) => Ok(value),
4750 GetOutcome::Text(text) => self
4751 .allocate(HeapEntry::String(text))
4752 .map_err(EvalFailure::Runtime),
4753 GetOutcome::Getter(getter) => self.call_value(getter, object, &[]),
4754 }
4755 }
4756
4757 pub(crate) fn set_data_property(
4758 &mut self,
4759 object: Value,
4760 name: &str,
4761 value: Value,
4762 ) -> Result<(), EvalFailure> {
4763 self.set_data_property_key(
4764 object,
4765 PropertyKey::Named(EcmaString::from_utf8(name)),
4766 value,
4767 )
4768 }
4769
4770 pub(crate) fn set_data_property_key(
4771 &mut self,
4772 object: Value,
4773 key: PropertyKey,
4774 value: Value,
4775 ) -> Result<(), EvalFailure> {
4776 match self.resolve_set(object, key, value)? {
4777 SetOutcome::Done => Ok(()),
4778 SetOutcome::Setter(setter) => {
4779 self.call_value(setter, object, &[value])?;
4780 Ok(())
4781 }
4782 }
4783 }
4784
4785 pub(crate) fn is_callable(&self, value: Value) -> Result<bool, EvalFailure> {
4786 Ok(!matches!(
4787 self.callee_kind(value).map_err(EvalFailure::Runtime)?,
4788 CalleeKind::NotCallable
4789 ))
4790 }
4791
4792 pub(crate) fn box_primitive(&mut self, value: Value) -> Result<Value, EvalFailure> {
4793 let prototype = match value.decode() {
4794 Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
4795 Some(Decoded::Number(_) | Decoded::Int32(_)) => self.intrinsics.number_prototype,
4796 Some(Decoded::HeapRef(_)) if self.string_value(value).is_some() => {
4797 self.intrinsics.string_prototype
4798 }
4799 _ => self.intrinsics.object_prototype,
4800 };
4801 self.allocate(HeapEntry::Object {
4802 properties: PropertyMap::default(),
4803 prototype: Some(prototype),
4804 boxed_primitive: Some(value),
4805 extensible: true,
4806 })
4807 .map_err(EvalFailure::Runtime)
4808 }
4809
4810 pub(crate) fn unbox_primitive_or_self(&self, value: Value) -> Result<Value, EvalFailure> {
4811 let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4812 return Ok(value);
4813 };
4814 match self.heap[index] {
4815 HeapEntry::Object {
4816 boxed_primitive: Some(primitive),
4817 ..
4818 } => Ok(primitive),
4819 _ => Ok(value),
4820 }
4821 }
4822
4823 pub(crate) fn unbox_primitive(
4824 &self,
4825 value: Value,
4826 operation: &'static str,
4827 ) -> Result<Value, EvalFailure> {
4828 let unboxed = self.unbox_primitive_or_self(value)?;
4829 if unboxed == value && self.is_object(value) {
4830 Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }))
4831 } else {
4832 Ok(unboxed)
4833 }
4834 }
4835
4836 pub(crate) fn current_builtin_id(&self) -> Option<intrinsics::BuiltinId> {
4837 self.current_builtin_id
4838 }
4839
4840 pub(crate) fn throw_error(
4841 &mut self,
4842 id: intrinsics::BuiltinId,
4843 message: String,
4844 ) -> EvalFailure {
4845 let message = match self.allocate(HeapEntry::String(EcmaString::from_utf8(&message))) {
4846 Ok(value) => value,
4847 Err(kind) => return EvalFailure::Runtime(kind),
4848 };
4849 let mut properties = PropertyMap::default();
4850 properties.insert(
4851 PropertyKey::Named(EcmaString::from_utf8("message")),
4852 Property::Data {
4853 value: message,
4854 writable: true,
4855 enumerable: true,
4856 configurable: true,
4857 },
4858 );
4859 match self.allocate(HeapEntry::Object {
4860 properties,
4861 prototype: Some(self.intrinsics.error_prototype(id)),
4862 boxed_primitive: None,
4863 extensible: true,
4864 }) {
4865 Ok(value) => EvalFailure::ThrowValue(value),
4866 Err(kind) => EvalFailure::Runtime(kind),
4867 }
4868 }
4869
4870 pub(crate) fn has_own_property_key(
4871 &self,
4872 object: Value,
4873 key: &PropertyKey,
4874 ) -> Result<bool, EvalFailure> {
4875 let Some(index) = self.runtime_slot(object).map_err(EvalFailure::Runtime)? else {
4876 return Ok(false);
4877 };
4878 Ok(self.own_get(index, key).is_some())
4879 }
4880
4881 pub(crate) fn call_value(
4882 &mut self,
4883 callee: Value,
4884 this_value: Value,
4885 arguments: &[Value],
4886 ) -> Result<Value, EvalFailure> {
4887 let mut callee = callee;
4888 let mut this_value = this_value;
4889 let mut arguments = Cow::Borrowed(arguments);
4890 loop {
4891 match self.callee_kind(callee).map_err(EvalFailure::Runtime)? {
4892 CalleeKind::Builtin { id } => {
4893 match self.call_builtin(id, this_value, arguments.as_ref(), false)? {
4894 intrinsics::BuiltinOutcome::Value(value) => return Ok(value),
4895 intrinsics::BuiltinOutcome::Call {
4896 callee: next,
4897 this_value: next_this,
4898 arguments: next_arguments,
4899 } => {
4900 callee = next;
4901 this_value = next_this;
4902 arguments = Cow::Owned(next_arguments);
4903 }
4904 intrinsics::BuiltinOutcome::GeneratorNext {
4905 generator,
4906 resume_value,
4907 } => return self.resume_generator(generator, resume_value),
4908 intrinsics::BuiltinOutcome::ConstructCall { .. } => {
4909 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4910 operation: "call",
4911 }));
4912 }
4913 }
4914 }
4915 CalleeKind::Runtime { target, captures } => {
4916 let flags = self.module_code(target.module).functions()
4917 [target.function.get() as usize]
4918 .flags();
4919 if flags.is_generator && !flags.is_async {
4920 return self
4921 .create_generator(GeneratorStart {
4922 target,
4923 captures,
4924 this_value,
4925 new_target: Value::UNDEFINED,
4926 args: arguments.as_ref().to_vec(),
4927 })
4928 .map_err(EvalFailure::Runtime);
4929 }
4930 if flags.is_async && !flags.is_generator {
4931 return self.start_async_call(
4932 target,
4933 &captures,
4934 this_value,
4935 Value::UNDEFINED,
4936 arguments.as_ref(),
4937 );
4938 }
4939 let stop_depth = self.frames.len();
4940 let return_to = self.frames.last().map(|frame| ReturnTo {
4941 destination: None,
4942 call_pc: frame.pc,
4943 constructed: None,
4944 });
4945 self.push_frame(
4946 target,
4947 &captures,
4948 this_value,
4949 Value::UNDEFINED,
4950 arguments.as_ref(),
4951 return_to,
4952 )
4953 .map_err(|error| EvalFailure::Runtime(error.kind))?;
4954 self.callback_boundaries.push(stop_depth);
4955 let result = self.run_loop(stop_depth);
4956 self.callback_boundaries
4957 .pop()
4958 .expect("nested runtime callback owns its unwind boundary");
4959 return match result {
4960 Ok(None) => self.last_completion.take().ok_or(EvalFailure::Runtime(
4961 RuntimeErrorKind::InvalidValue {
4962 value: Value::UNDEFINED,
4963 },
4964 )),
4965 Ok(Some(execution)) => Ok(execution.value),
4966 Err(error) => {
4967 self.unwind_frames_to(stop_depth);
4968 match error.kind {
4969 RuntimeErrorKind::UncaughtThrow { value, .. } => {
4970 Err(EvalFailure::ThrowValue(value))
4971 }
4972 kind => Err(EvalFailure::Runtime(kind)),
4973 }
4974 }
4975 };
4976 }
4977 CalleeKind::Bound => {
4978 let bound = self
4979 .flatten_bound(callee, this_value, arguments.as_ref())
4980 .map_err(EvalFailure::Runtime)?;
4981 callee = bound.target;
4982 this_value = bound.this_value;
4983 arguments = Cow::Owned(bound.arguments);
4984 }
4985 CalleeKind::NotCallable => {
4986 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4987 operation: "call",
4988 }));
4989 }
4990 }
4991 }
4992 }
4993
4994 fn unwind_frames_to(&mut self, depth: usize) {
4995 while self.frames.len() > depth {
4996 let frame = self.frames.pop().expect("frame depth was checked");
4997 self.live_registers -= frame.registers.len();
4998 }
4999 }
5000
5001 fn complete_frame(&mut self, returned: Value) -> Option<Execution> {
5002 let frame = self.frames.pop().expect("an activation is executing");
5003 self.live_registers -= frame.registers.len();
5004 match frame.return_to {
5005 None => {
5006 let outcome = ExecutionOutcome {
5007 stdout: Vec::new(),
5008 exit_code: 0,
5009 };
5010 Some(Execution {
5011 outcome,
5012 value: returned,
5013 link: returned,
5014 entry_registers: frame.registers,
5015 })
5016 }
5017 Some(return_to) => {
5018 let value = match return_to.constructed {
5019 Some(object) if !self.is_object(returned) => object,
5020 _ => returned,
5021 };
5022 if let Some(destination) = return_to.destination {
5023 self.frames.last_mut().expect("callee has caller").registers[destination] =
5024 value;
5025 } else {
5026 self.last_completion = Some(value);
5027 }
5028 None
5029 }
5030 }
5031 }
5032
5033 fn resolve_failure(&mut self, failure: EvalFailure, pc: usize) -> Result<(), RuntimeError> {
5034 match failure {
5035 EvalFailure::Throw(origin) => self.throw(Value::UNDEFINED, origin, pc),
5036 EvalFailure::ThrowValue(value) => self.throw(value, ThrowOrigin::Bytecode, pc),
5037 EvalFailure::ThrowValueOrigin { value, origin } => self.throw(value, origin, pc),
5038 EvalFailure::Runtime(kind) => Err(self.error_here_at(kind, pc)),
5039 }
5040 }
5041
5042 fn throw_type(&mut self, operation: &'static str, pc: usize) -> Result<(), RuntimeError> {
5043 self.throw(Value::UNDEFINED, ThrowOrigin::TypeError { operation }, pc)
5044 }
5045
5046 fn throw(
5047 &mut self,
5048 value: Value,
5049 origin: ThrowOrigin,
5050 faulting_pc: usize,
5051 ) -> Result<(), RuntimeError> {
5052 let site_module = self
5053 .frames
5054 .last()
5055 .expect("an activation is executing")
5056 .module;
5057 let site_function = self
5058 .frames
5059 .last()
5060 .expect("an activation is executing")
5061 .function;
5062 let mut search_pc = faulting_pc;
5063 loop {
5064 if self
5065 .callback_boundaries
5066 .last()
5067 .is_some_and(|boundary| self.frames.len() == *boundary)
5068 {
5069 return Err(self.error_at_in_module(
5070 RuntimeErrorKind::UncaughtThrow { value, origin },
5071 site_module,
5072 site_function,
5073 faulting_pc,
5074 ));
5075 }
5076 let frame_index = self.frames.len() - 1;
5077 let function_index = self.frames[frame_index].function;
5078 let module = self.frames[frame_index].module;
5079 let function = &self.module_code(module).functions()[function_index];
5080 if let Some(handler) = innermost_handler(function, search_pc) {
5081 let frame = &mut self.frames[frame_index];
5082 frame.registers[handler.catch_register.get() as usize] = value;
5083 frame.pc = handler.handler.get() as usize;
5084 return Ok(());
5085 }
5086 let frame = self.frames.pop().expect("throw walks live frames");
5087 self.live_registers -= frame.registers.len();
5088 match frame.return_to {
5089 Some(return_to) => search_pc = return_to.call_pc,
5090 None => {
5091 return Err(self.error_at_in_module(
5092 RuntimeErrorKind::UncaughtThrow { value, origin },
5093 site_module,
5094 site_function,
5095 faulting_pc,
5096 ));
5097 }
5098 }
5099 }
5100 }
5101
5102 fn error_here(&self, kind: RuntimeErrorKind) -> RuntimeError {
5103 let frame = self.frames.last().expect("an activation is executing");
5104 self.error_at(kind, frame.function, frame.pc)
5105 }
5106
5107 fn error_here_at(&self, kind: RuntimeErrorKind, pc: usize) -> RuntimeError {
5108 let function = self
5109 .frames
5110 .last()
5111 .expect("an activation is executing")
5112 .function;
5113 self.error_at(kind, function, pc)
5114 }
5115
5116 fn error_at(&self, kind: RuntimeErrorKind, function: usize, pc: usize) -> RuntimeError {
5117 self.error_at_in_module(kind, self.active_module_id(), function, pc)
5118 }
5119
5120 pub(crate) fn error_at_in_module(
5121 &self,
5122 kind: RuntimeErrorKind,
5123 module: ModuleId,
5124 function: usize,
5125 pc: usize,
5126 ) -> RuntimeError {
5127 let code = self.module_code(module);
5128 let metadata = &code.functions()[function];
5129 let function_name =
5130 metadata
5131 .name()
5132 .and_then(|id| match &code.constants()[id.get() as usize] {
5133 Constant::String(name) => Some(name.clone()),
5134 _ => None,
5135 });
5136 RuntimeError {
5137 kind,
5138 function: FunctionId::new(function as u32),
5139 pc: Pc::new(pc as u32),
5140 source: RuntimeSource {
5141 function_name,
5142 instruction: metadata.code()[pc],
5143 },
5144 }
5145 }
5146
5147 fn to_property_key(&self, value: Value) -> Result<PropertyKey, EvalFailure> {
5153 match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
5154 Some(index) => match &self.heap[index] {
5155 HeapEntry::String(text) => Ok(PropertyKey::Named(text.clone())),
5156 HeapEntry::Symbol { .. } => Ok(PropertyKey::Symbol(index as u32)),
5157 HeapEntry::PrivateName { .. } => Ok(PropertyKey::Private(index as u32)),
5158 _ => Ok(PropertyKey::Named(self.value_to_string(value, 0)?)),
5159 },
5160 None => Ok(PropertyKey::Named(self.value_to_string(value, 0)?)),
5161 }
5162 }
5163
5164 fn resolve_get(&mut self, object: Value, key: &PropertyKey) -> Result<GetOutcome, EvalFailure> {
5167 let slot = self.runtime_slot(object).map_err(EvalFailure::Runtime)?;
5168 let start = match slot {
5169 Some(index) => {
5170 if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5171 let PropertyKey::Named(name) = key else {
5172 return Ok(GetOutcome::Value(Value::UNDEFINED));
5173 };
5174 let text = name
5175 .to_utf8_strict()
5176 .ok()
5177 .and_then(|name| self.host.env(&name))
5178 .map(EcmaString::from_utf8);
5179 return match text {
5180 Some(text) => self
5181 .allocate(HeapEntry::String(text))
5182 .map(GetOutcome::Value)
5183 .map_err(EvalFailure::Runtime),
5184 None => Ok(GetOutcome::Value(Value::UNDEFINED)),
5185 };
5186 }
5187 if let Some(found) = self.primitive_get(index, key) {
5188 return self.found_outcome(found);
5189 }
5190 match self.heap[index] {
5191 HeapEntry::String(_) => self
5192 .runtime_slot(self.intrinsics.string_prototype)
5193 .map_err(EvalFailure::Runtime)?,
5194 HeapEntry::BigInt(_) | HeapEntry::PrivateName { .. } => self
5195 .runtime_slot(self.intrinsics.object_prototype)
5196 .map_err(EvalFailure::Runtime)?,
5197 HeapEntry::Symbol { .. } => self
5198 .runtime_slot(self.intrinsics.builtins.symbol_prototype())
5199 .map_err(EvalFailure::Runtime)?,
5200 _ => Some(index),
5201 }
5202 }
5203 None => {
5204 let prototype = match object.decode() {
5205 Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
5206 Some(Decoded::Number(_) | Decoded::Int32(_)) => {
5207 self.intrinsics.number_prototype
5208 }
5209 _ => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5210 };
5211 self.runtime_slot(prototype).map_err(EvalFailure::Runtime)?
5212 }
5213 };
5214 let Some(mut node) = start else {
5215 return Ok(GetOutcome::Value(Value::UNDEFINED));
5216 };
5217 for _ in 0..=self.heap.len() {
5218 if let Some(found) = self.own_get(node, key) {
5219 return self.found_outcome(found);
5220 }
5221 match self.prototype_index(node)? {
5222 Some(next) => node = next,
5223 None => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5224 }
5225 }
5226 Ok(GetOutcome::Value(Value::UNDEFINED))
5227 }
5228
5229 fn resolve_get_ascii(&mut self, object: Value, name: &str) -> Result<GetOutcome, EvalFailure> {
5230 debug_assert!(name.is_ascii());
5231 let slot = self.runtime_slot(object).map_err(EvalFailure::Runtime)?;
5232 let start = match slot {
5233 Some(index) => {
5234 if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5235 return match self.host.env(name).map(EcmaString::from_utf8) {
5236 Some(text) => self
5237 .allocate(HeapEntry::String(text))
5238 .map(GetOutcome::Value)
5239 .map_err(EvalFailure::Runtime),
5240 None => Ok(GetOutcome::Value(Value::UNDEFINED)),
5241 };
5242 }
5243 if let HeapEntry::String(text) = &self.heap[index] {
5244 if name == "length" {
5245 return Ok(GetOutcome::Value(number_value(text.len_units() as f64)));
5246 }
5247 if let Some(offset) = array_index_ascii(name)
5248 && let Some(unit) = text.unit_at(offset as usize)
5249 {
5250 return Ok(GetOutcome::Text(EcmaString::from_units(&[unit])));
5251 }
5252 }
5253 match self.heap[index] {
5254 HeapEntry::String(_) => self
5255 .runtime_slot(self.intrinsics.string_prototype)
5256 .map_err(EvalFailure::Runtime)?,
5257 HeapEntry::BigInt(_) | HeapEntry::PrivateName { .. } => self
5258 .runtime_slot(self.intrinsics.object_prototype)
5259 .map_err(EvalFailure::Runtime)?,
5260 HeapEntry::Symbol { .. } => self
5261 .runtime_slot(self.intrinsics.builtins.symbol_prototype())
5262 .map_err(EvalFailure::Runtime)?,
5263 _ => Some(index),
5264 }
5265 }
5266 None => {
5267 let prototype = match object.decode() {
5268 Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
5269 Some(Decoded::Number(_) | Decoded::Int32(_)) => {
5270 self.intrinsics.number_prototype
5271 }
5272 _ => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5273 };
5274 self.runtime_slot(prototype).map_err(EvalFailure::Runtime)?
5275 }
5276 };
5277 let Some(mut node) = start else {
5278 return Ok(GetOutcome::Value(Value::UNDEFINED));
5279 };
5280 for _ in 0..=self.heap.len() {
5281 if let Some(found) = self.own_get_ascii(node, name) {
5282 return self.found_outcome(found);
5283 }
5284 match self.prototype_index(node)? {
5285 Some(next) => node = next,
5286 None => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5287 }
5288 }
5289 Ok(GetOutcome::Value(Value::UNDEFINED))
5290 }
5291
5292 fn found_outcome(&mut self, found: Found) -> Result<GetOutcome, EvalFailure> {
5293 match found {
5294 Found::Value(Value::UNINITIALIZED) => {
5295 let id = self
5296 .intrinsics
5297 .builtins
5298 .id_named("ReferenceError")
5299 .expect("ReferenceError intrinsic is installed");
5300 match self.throw_error(
5301 id,
5302 "Cannot access lexical binding before initialization".into(),
5303 ) {
5304 EvalFailure::ThrowValue(value) => Err(EvalFailure::ThrowValueOrigin {
5305 value,
5306 origin: ThrowOrigin::ReferenceError {
5307 operation: "lexical binding is uninitialized",
5308 },
5309 }),
5310 failure => Err(failure),
5311 }
5312 }
5313 Found::Value(value) => Ok(GetOutcome::Value(value)),
5314 Found::Text(text) => Ok(GetOutcome::Text(text)),
5315 Found::Getter(getter) => Ok(GetOutcome::Getter(getter)),
5316 Found::Failure(kind) => Err(EvalFailure::Runtime(kind)),
5317 Found::NoGetter => Ok(GetOutcome::Value(Value::UNDEFINED)),
5318 }
5319 }
5320
5321 fn primitive_get(&self, index: usize, key: &PropertyKey) -> Option<Found> {
5322 if let HeapEntry::String(text) = &self.heap[index]
5323 && let PropertyKey::Named(name) = key
5324 {
5325 if name.eq_ascii("length") {
5326 return Some(Found::Value(number_value(text.len_units() as f64)));
5327 }
5328 if let Some(offset) = array_index(name)
5329 && let Some(unit) = text.unit_at(offset as usize)
5330 {
5331 return Some(Found::Text(EcmaString::from_units(&[unit])));
5332 }
5333 }
5334 None
5335 }
5336 fn own_get_ascii(&self, index: usize, name: &str) -> Option<Found> {
5337 debug_assert!(name.is_ascii());
5338 let slot = |value| self.runtime_slot(value).ok().flatten();
5339 if slot(self.intrinsics.object_prototype) == Some(index) && name == "toString" {
5340 return Some(Found::Value(self.intrinsics.object_to_string()));
5341 }
5342 match &self.heap[index] {
5343 HeapEntry::Object { properties, .. }
5344 | HeapEntry::Generator { properties, .. }
5345 | HeapEntry::Script { properties, .. }
5346 | HeapEntry::NativeFunction { properties, .. }
5347 | HeapEntry::Date { properties, .. }
5348 | HeapEntry::BuiltinIterator { properties, .. }
5349 | HeapEntry::Collection { properties, .. }
5350 | HeapEntry::Promise { properties, .. }
5351 | HeapEntry::Timeout { properties, .. } => property_lookup_ascii(properties, name),
5352 HeapEntry::Array {
5353 elements,
5354 properties,
5355 ..
5356 } => {
5357 if name == "length" {
5358 return Some(Found::Value(number_value(elements.len() as f64)));
5359 }
5360 if let Some(offset) = array_index_ascii(name)
5361 && let Some(element) = elements.get(offset as usize)
5362 && *element != Value::HOLE
5363 {
5364 return Some(Found::Value(*element));
5365 }
5366 property_lookup_ascii(properties, name)
5367 }
5368 HeapEntry::Function {
5369 module,
5370 function,
5371 properties,
5372 ..
5373 } => {
5374 if let Some(found) = property_lookup_ascii(properties, name) {
5375 return Some(found);
5376 }
5377 let metadata = &self.module_code(*module).functions()[function.get() as usize];
5378 if name == "length" {
5379 return Some(Found::Value(
5380 number_value(metadata.parameter_count() as f64),
5381 ));
5382 }
5383 if name == "name" {
5384 return Some(Found::Text(
5385 metadata
5386 .name()
5387 .map(|id| self.constant_text(*module, id).clone())
5388 .unwrap_or_default(),
5389 ));
5390 }
5391 None
5392 }
5393 HeapEntry::ModuleNamespace { module } => {
5394 let key = self
5395 .program_module(*module)
5396 .exports
5397 .iter()
5398 .map(|export| self.constant_text(*module, export.name))
5399 .find(|candidate| candidate.eq_ascii(name))?
5400 .clone();
5401 match self.namespace_export(*module, &key) {
5402 Ok(Some(value)) => Some(Found::Value(value)),
5403 Ok(None) => None,
5404 Err(kind) => Some(Found::Failure(kind)),
5405 }
5406 }
5407 HeapEntry::ExternalModuleNamespace { specifier } => {
5408 let export = self.registry.external[specifier]
5409 .exports
5410 .iter()
5411 .find_map(|(candidate, export)| candidate.eq_ascii(name).then_some(export))?;
5412 let cell = export
5413 .cell
5414 .expect("external namespace exports link before evaluation");
5415 Some(Found::Value(self.registry.cells[cell.0].value))
5416 }
5417 HeapEntry::RegExp {
5418 pattern,
5419 flags,
5420 properties,
5421 ..
5422 } => {
5423 if let Some(found) = property_lookup_ascii(properties, name) {
5424 return Some(found);
5425 }
5426 let flag = |unit| {
5427 Found::Value(Value::boolean(flags.as_units().contains(&u16::from(unit))))
5428 };
5429 match name {
5430 "source" => Some(Found::Text(crate::intrinsics::builtins::canonical_source(
5431 pattern,
5432 ))),
5433 "flags" => Some(Found::Text(flags.clone())),
5434 "global" => Some(flag(b'g')),
5435 "ignoreCase" => Some(flag(b'i')),
5436 "multiline" => Some(flag(b'm')),
5437 "sticky" => Some(flag(b'y')),
5438 "unicode" => Some(flag(b'u')),
5439 "dotAll" => Some(flag(b's')),
5440 "lastIndex" => Some(Found::Value(Value::int32(0))),
5441 _ => None,
5442 }
5443 }
5444 HeapEntry::HashState { update, digest, .. } => match name {
5445 "update" => Some(Found::Value(*update)),
5446 "digest" => Some(Found::Value(*digest)),
5447 _ => None,
5448 },
5449 HeapEntry::ProcessEnv { .. }
5450 | HeapEntry::String(_)
5451 | HeapEntry::BigInt(_)
5452 | HeapEntry::Symbol { .. }
5453 | HeapEntry::PrivateName { .. }
5454 | HeapEntry::Iterator { .. }
5455 | HeapEntry::PromiseResolver { .. }
5456 | HeapEntry::PromiseFinally { .. }
5457 | HeapEntry::PromiseAll { .. }
5458 | HeapEntry::AsyncActivation { .. }
5459 | HeapEntry::PromiseAllElement { .. } => None,
5460 }
5461 }
5462
5463 fn own_get(&self, index: usize, key: &PropertyKey) -> Option<Found> {
5466 if let PropertyKey::Named(name) = key {
5467 let slot = |value| self.runtime_slot(value).ok().flatten();
5468 if slot(self.intrinsics.object_prototype) == Some(index) && name.eq_ascii("toString") {
5469 return Some(Found::Value(self.intrinsics.object_to_string()));
5470 }
5471 }
5472 match &self.heap[index] {
5473 HeapEntry::Object { properties, .. }
5474 | HeapEntry::Generator { properties, .. }
5475 | HeapEntry::Script { properties, .. }
5476 | HeapEntry::Date { properties, .. }
5477 | HeapEntry::BuiltinIterator { properties, .. }
5478 | HeapEntry::Collection { properties, .. }
5479 | HeapEntry::Promise { properties, .. }
5480 | HeapEntry::Timeout { properties, .. } => property_lookup(properties, key),
5481 HeapEntry::Array {
5482 elements,
5483 properties,
5484 ..
5485 } => {
5486 if let PropertyKey::Named(name) = key {
5487 if name.eq_ascii("length") {
5488 return Some(Found::Value(number_value(elements.len() as f64)));
5489 }
5490 if let Some(offset) = array_index(name)
5491 && let Some(element) = elements.get(offset as usize)
5492 && *element != Value::HOLE
5493 {
5494 return Some(Found::Value(*element));
5495 }
5496 }
5497 property_lookup(properties, key)
5498 }
5499 HeapEntry::Function {
5500 module,
5501 function,
5502 properties,
5503 ..
5504 } => {
5505 if let Some(found) = property_lookup(properties, key) {
5506 return Some(found);
5507 }
5508 if let PropertyKey::Named(name) = key {
5509 let metadata = &self.module_code(*module).functions()[function.get() as usize];
5510 if name.eq_ascii("length") {
5511 return Some(Found::Value(
5512 number_value(metadata.parameter_count() as f64),
5513 ));
5514 }
5515 if name.eq_ascii("name") {
5516 return Some(Found::Text(
5517 metadata
5518 .name()
5519 .map(|id| self.constant_text(*module, id).clone())
5520 .unwrap_or_default(),
5521 ));
5522 }
5523 }
5524 None
5525 }
5526 HeapEntry::ModuleNamespace { module } => {
5527 let PropertyKey::Named(name) = key else {
5528 return None;
5529 };
5530 match self.namespace_export(*module, name) {
5531 Ok(Some(value)) => Some(Found::Value(value)),
5532 Ok(None) => None,
5533 Err(kind) => Some(Found::Failure(kind)),
5534 }
5535 }
5536 HeapEntry::ExternalModuleNamespace { specifier } => {
5537 let PropertyKey::Named(name) = key else {
5538 return None;
5539 };
5540 let export = self.registry.external[specifier].exports.get(name)?;
5541 Some(Found::Value(export.cell.map_or(export.value, |cell| {
5542 self.registry.cells[cell.0].value
5543 })))
5544 }
5545 HeapEntry::NativeFunction { properties, .. } => property_lookup(properties, key),
5546 HeapEntry::RegExp {
5547 pattern,
5548 flags,
5549 properties,
5550 ..
5551 } => {
5552 if let Some(found) = property_lookup(properties, key) {
5553 return Some(found);
5554 }
5555 if let PropertyKey::Named(name) = key {
5556 let flag = |ascii: &str| {
5557 Found::Value(Value::boolean(
5558 flags.as_units().contains(&u16::from(ascii.as_bytes()[0])),
5559 ))
5560 };
5561 if name.eq_ascii("source") {
5562 return Some(Found::Text(crate::intrinsics::builtins::canonical_source(
5563 pattern,
5564 )));
5565 }
5566 if name.eq_ascii("flags") {
5567 return Some(Found::Text(flags.clone()));
5568 }
5569 if name.eq_ascii("global") {
5570 return Some(flag("g"));
5571 }
5572 if name.eq_ascii("ignoreCase") {
5573 return Some(flag("i"));
5574 }
5575 if name.eq_ascii("multiline") {
5576 return Some(flag("m"));
5577 }
5578 if name.eq_ascii("sticky") {
5579 return Some(flag("y"));
5580 }
5581 if name.eq_ascii("unicode") {
5582 return Some(flag("u"));
5583 }
5584 if name.eq_ascii("dotAll") {
5585 return Some(flag("s"));
5586 }
5587 if name.eq_ascii("lastIndex") {
5588 return Some(Found::Value(Value::int32(0)));
5589 }
5590 }
5591 None
5592 }
5593 HeapEntry::HashState { update, digest, .. } => {
5594 let PropertyKey::Named(name) = key else {
5595 return None;
5596 };
5597 if name.eq_ascii("update") {
5598 Some(Found::Value(*update))
5599 } else if name.eq_ascii("digest") {
5600 Some(Found::Value(*digest))
5601 } else {
5602 None
5603 }
5604 }
5605 HeapEntry::ProcessEnv { .. }
5606 | HeapEntry::String(_)
5607 | HeapEntry::BigInt(_)
5608 | HeapEntry::Symbol { .. }
5609 | HeapEntry::PrivateName { .. }
5610 | HeapEntry::Iterator { .. }
5611 | HeapEntry::PromiseResolver { .. }
5612 | HeapEntry::PromiseFinally { .. }
5613 | HeapEntry::PromiseAll { .. }
5614 | HeapEntry::AsyncActivation { .. }
5615 | HeapEntry::PromiseAllElement { .. } => None,
5616 }
5617 }
5618
5619 fn namespace_export(
5620 &self,
5621 module: ModuleId,
5622 name: &EcmaString,
5623 ) -> Result<Option<Value>, RuntimeErrorKind> {
5624 if module.get() as usize >= self.dynamic_base {
5625 return Ok(None);
5626 }
5627 match self.program().resolve_export(module, name) {
5628 Some(ResolvedExport::Local { module, binding }) => {
5629 let cell = self.registry.modules[module.get() as usize].binding_cells
5630 [binding.get() as usize]
5631 .expect("verified export resolves to a linked cell");
5632 let value = self.registry.cells[cell.0].value;
5633 if value.is_uninitialized() {
5634 Err(RuntimeErrorKind::TemporalDeadZone { module, binding })
5635 } else {
5636 Ok(Some(value))
5637 }
5638 }
5639 Some(ResolvedExport::External { module, edge, name }) => {
5640 let Some(specifier) = self.external_specifier(module, edge) else {
5641 return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5642 };
5643 let name = self.constant_text(module, name);
5644 let Some(export) = self.registry.external[&specifier].exports.get(name) else {
5645 return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5646 };
5647 let Some(cell) = export.cell else {
5648 return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5649 };
5650 Ok(Some(self.registry.cells[cell.0].value))
5651 }
5652 None => Ok(None),
5653 }
5654 }
5655
5656 fn own_data_property(&self, index: usize, name: &str) -> Option<Value> {
5657 let properties = match &self.heap[index] {
5658 HeapEntry::Object { properties, .. }
5659 | HeapEntry::Generator { properties, .. }
5660 | HeapEntry::Script { properties, .. }
5661 | HeapEntry::Array { properties, .. }
5662 | HeapEntry::Function { properties, .. }
5663 | HeapEntry::NativeFunction { properties, .. }
5664 | HeapEntry::RegExp { properties, .. }
5665 | HeapEntry::Date { properties, .. }
5666 | HeapEntry::BuiltinIterator { properties, .. }
5667 | HeapEntry::Collection { properties, .. }
5668 | HeapEntry::Promise { properties, .. }
5669 | HeapEntry::Timeout { properties, .. } => properties,
5670 _ => return None,
5671 };
5672 match properties.get_ascii(name) {
5673 Some(Property::Data { value, .. }) => Some(*value),
5674 _ => None,
5675 }
5676 }
5677
5678 fn prototype_index(&self, index: usize) -> Result<Option<usize>, EvalFailure> {
5679 let prototype = match &self.heap[index] {
5680 HeapEntry::Object { prototype, .. }
5681 | HeapEntry::Generator { prototype, .. }
5682 | HeapEntry::Script { prototype, .. }
5683 | HeapEntry::Array { prototype, .. }
5684 | HeapEntry::Function { prototype, .. }
5685 | HeapEntry::RegExp { prototype, .. }
5686 | HeapEntry::Date { prototype, .. }
5687 | HeapEntry::BuiltinIterator { prototype, .. }
5688 | HeapEntry::Collection { prototype, .. }
5689 | HeapEntry::Promise { prototype, .. }
5690 | HeapEntry::Timeout { prototype, .. }
5691 | HeapEntry::ProcessEnv { prototype, .. } => *prototype,
5692 HeapEntry::NativeFunction { .. } => Some(self.intrinsics.function_prototype),
5693 _ => None,
5694 };
5695 match prototype {
5696 Some(value) => self.runtime_slot(value).map_err(EvalFailure::Runtime),
5697 None => Ok(None),
5698 }
5699 }
5700
5701 pub(crate) fn inherits_from_prototype(
5702 &self,
5703 value: Value,
5704 prototype: Value,
5705 ) -> Result<bool, EvalFailure> {
5706 let Some(mut current) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
5707 return Ok(false);
5708 };
5709 let Some(target) = self.runtime_slot(prototype).map_err(EvalFailure::Runtime)? else {
5710 return Ok(false);
5711 };
5712 let mut traversed = 0;
5713 while let Some(next) = self.prototype_index(current)? {
5714 if next == target {
5715 return Ok(true);
5716 }
5717 current = next;
5718 traversed += 1;
5719 if traversed > self.heap.len() {
5720 return Ok(false);
5721 }
5722 }
5723 Ok(false)
5724 }
5725
5726 fn resolve_set(
5729 &mut self,
5730 object: Value,
5731 key: PropertyKey,
5732 value: Value,
5733 ) -> Result<SetOutcome, EvalFailure> {
5734 match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
5735 Some(index) => {
5736 if matches!(self.heap[index], HeapEntry::ModuleNamespace { .. }) {
5737 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5738 operation: "assign to module namespace",
5739 }));
5740 }
5741 if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5742 let PropertyKey::Named(name) = &key else {
5743 return Ok(SetOutcome::Done);
5744 };
5745 let Ok(name) = name.to_utf8_strict() else {
5746 return Ok(SetOutcome::Done);
5747 };
5748 let text = self.to_string(value)?;
5749 let text = crate::host_objects::env_value_text_lossy(&text);
5750 self.host.set_env(&name, &text);
5751 return Ok(SetOutcome::Done);
5752 }
5753 if let Some(setter) = self.find_setter(index, &key)? {
5754 return Ok(match setter {
5755 Some(setter) => SetOutcome::Setter(setter),
5756 None => SetOutcome::Done,
5757 });
5758 }
5759 self.set_own_data(index, key, value)?;
5760 Ok(SetOutcome::Done)
5761 }
5762 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5763 operation: "set property on primitive",
5764 })),
5765 }
5766 }
5767
5768 fn find_setter(
5769 &self,
5770 index: usize,
5771 key: &PropertyKey,
5772 ) -> Result<Option<Option<Value>>, EvalFailure> {
5773 if self.own_has_non_accessor(index, key) {
5774 return Ok(None);
5775 }
5776 let mut node = index;
5777 let mut guard = 0;
5778 loop {
5779 let accessor = match &self.heap[node] {
5780 HeapEntry::Object { properties, .. }
5781 | HeapEntry::Generator { properties, .. }
5782 | HeapEntry::Script { properties, .. }
5783 | HeapEntry::Array { properties, .. }
5784 | HeapEntry::Function { properties, .. }
5785 | HeapEntry::NativeFunction { properties, .. }
5786 | HeapEntry::RegExp { properties, .. }
5787 | HeapEntry::Date { properties, .. }
5788 | HeapEntry::BuiltinIterator { properties, .. }
5789 | HeapEntry::Collection { properties, .. }
5790 | HeapEntry::Promise { properties, .. }
5791 | HeapEntry::Timeout { properties, .. } => match properties.get(key) {
5792 Some(Property::Accessor { setter, .. }) => Some(Some(*setter)),
5793 Some(Property::Data { .. }) => Some(None),
5794 None => None,
5795 },
5796 _ => None,
5797 };
5798 match accessor {
5799 Some(Some(setter)) => return Ok(Some(setter)),
5800 Some(None) => return Ok(None),
5801 None => {}
5802 }
5803 match self.prototype_index(node)? {
5804 Some(next) => {
5805 node = next;
5806 guard += 1;
5807 if guard > self.heap.len() + 1 {
5808 return Ok(None);
5809 }
5810 }
5811 None => return Ok(None),
5812 }
5813 }
5814 }
5815
5816 fn own_has_non_accessor(&self, index: usize, key: &PropertyKey) -> bool {
5817 match &self.heap[index] {
5818 HeapEntry::Array { elements, .. } => {
5819 if let PropertyKey::Named(name) = key {
5820 if name.eq_ascii("length") {
5821 return true;
5822 }
5823 if let Some(offset) = array_index(name) {
5824 return elements
5825 .get(offset as usize)
5826 .is_some_and(|element| *element != Value::HOLE);
5827 }
5828 }
5829 false
5830 }
5831 HeapEntry::Function { .. } => {
5832 (key.eq_ascii("length") || key.eq_ascii("name"))
5833 && match key {
5834 PropertyKey::Named(name) if name.eq_ascii("length") => {
5835 self.own_data_property(index, "length").is_none()
5836 }
5837 PropertyKey::Named(_) => self.own_data_property(index, "name").is_none(),
5838 _ => false,
5839 }
5840 }
5841 _ => false,
5842 }
5843 }
5844
5845 fn set_own_data(
5846 &mut self,
5847 index: usize,
5848 key: PropertyKey,
5849 value: Value,
5850 ) -> Result<(), EvalFailure> {
5851 if matches!(key, PropertyKey::Named(ref name) if name.eq_ascii("length"))
5852 && matches!(self.heap[index], HeapEntry::Array { .. })
5853 {
5854 let HeapEntry::Array {
5855 elements,
5856 properties,
5857 length_writable,
5858 ..
5859 } = &mut self.heap[index]
5860 else {
5861 unreachable!("array checked above");
5862 };
5863 return array_set_length(
5864 elements,
5865 properties,
5866 *length_writable,
5867 value,
5868 "set array length",
5869 );
5870 }
5871 if let HeapEntry::Array {
5872 elements,
5873 length_writable,
5874 ..
5875 } = &self.heap[index]
5876 && let Some(offset) = key.as_string().and_then(array_index)
5877 && offset as usize >= elements.len()
5878 && !*length_writable
5879 {
5880 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5881 operation: "add index beyond non-writable array length",
5882 }));
5883 }
5884 let (properties, extensible, virtual_exists) = match &self.heap[index] {
5885 HeapEntry::Object {
5886 properties,
5887 extensible,
5888 ..
5889 }
5890 | HeapEntry::Generator {
5891 properties,
5892 extensible,
5893 ..
5894 }
5895 | HeapEntry::Script {
5896 properties,
5897 extensible,
5898 ..
5899 }
5900 | HeapEntry::Function {
5901 properties,
5902 extensible,
5903 ..
5904 }
5905 | HeapEntry::NativeFunction {
5906 properties,
5907 extensible,
5908 ..
5909 }
5910 | HeapEntry::RegExp {
5911 properties,
5912 extensible,
5913 ..
5914 }
5915 | HeapEntry::Date {
5916 properties,
5917 extensible,
5918 ..
5919 }
5920 | HeapEntry::BuiltinIterator {
5921 properties,
5922 extensible,
5923 ..
5924 }
5925 | HeapEntry::Collection {
5926 properties,
5927 extensible,
5928 ..
5929 }
5930 | HeapEntry::Promise {
5931 properties,
5932 extensible,
5933 ..
5934 } => (Some(properties), *extensible, false),
5935 HeapEntry::Array {
5936 elements,
5937 properties,
5938 extensible,
5939 ..
5940 } => {
5941 let virtual_exists = key.as_string().is_some_and(|name| {
5942 name.eq_ascii("length")
5943 || array_index(name).is_some_and(|offset| {
5944 elements
5945 .get(offset as usize)
5946 .is_some_and(|element| *element != Value::HOLE)
5947 })
5948 });
5949 (Some(properties), *extensible, virtual_exists)
5950 }
5951 _ => (None, true, false),
5952 };
5953 if let Some(property) = properties.and_then(|properties| properties.get(&key)) {
5954 match property {
5955 Property::Data {
5956 writable: false, ..
5957 }
5958 | Property::Accessor { .. } => {
5959 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5960 operation: "assign to read only property",
5961 }));
5962 }
5963 Property::Data { writable: true, .. } => {}
5964 }
5965 } else if !extensible && !virtual_exists {
5966 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5967 operation: "add property to non-extensible object",
5968 }));
5969 }
5970
5971 let growth = match &self.heap[index] {
5972 HeapEntry::Object { properties, .. }
5973 | HeapEntry::Generator { properties, .. }
5974 | HeapEntry::Script { properties, .. }
5975 | HeapEntry::Function { properties, .. }
5976 | HeapEntry::NativeFunction { properties, .. }
5977 | HeapEntry::RegExp { properties, .. }
5978 | HeapEntry::Date { properties, .. }
5979 | HeapEntry::BuiltinIterator { properties, .. }
5980 | HeapEntry::Collection { properties, .. }
5981 | HeapEntry::Promise { properties, .. }
5982 | HeapEntry::Timeout { properties, .. } => {
5983 usize::from(!properties.contains_key(&key)) * key.charge_bytes()
5984 }
5985 HeapEntry::Array {
5986 elements,
5987 properties,
5988 ..
5989 } => match &key {
5990 PropertyKey::Named(name) if name.eq_ascii("length") => 0,
5991 PropertyKey::Named(name) => {
5992 if let Some(offset) = array_index(name) {
5993 (offset as usize + 1).saturating_sub(elements.len()) * 8
5994 } else {
5995 usize::from(!properties.contains_key(&key)) * key.charge_bytes()
5996 }
5997 }
5998 PropertyKey::Symbol(_) | PropertyKey::Private(_) => {
5999 usize::from(!properties.contains_key(&key)) * key.charge_bytes()
6000 }
6001 },
6002 HeapEntry::String(_) | HeapEntry::BigInt(_) => {
6003 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6004 operation: "set property on primitive",
6005 }));
6006 }
6007 HeapEntry::Symbol { .. }
6008 | HeapEntry::PrivateName { .. }
6009 | HeapEntry::Iterator { .. }
6010 | HeapEntry::PromiseResolver { .. }
6011 | HeapEntry::PromiseFinally { .. }
6012 | HeapEntry::PromiseAll { .. }
6013 | HeapEntry::AsyncActivation { .. }
6014 | HeapEntry::PromiseAllElement { .. } => {
6015 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6016 operation: "set property on non-object",
6017 }));
6018 }
6019 HeapEntry::ProcessEnv { .. } => {
6020 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6021 operation: "set internal process environment",
6022 }));
6023 }
6024 HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
6025 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6026 operation: "assign to module namespace",
6027 }));
6028 }
6029 HeapEntry::HashState { .. } => {
6030 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6031 operation: "assign to hash state",
6032 }));
6033 }
6034 };
6035 self.charge_heap(growth).map_err(EvalFailure::Runtime)?;
6036 match &mut self.heap[index] {
6037 HeapEntry::Object { properties, .. }
6038 | HeapEntry::Generator { properties, .. }
6039 | HeapEntry::Script { properties, .. }
6040 | HeapEntry::Function { properties, .. }
6041 | HeapEntry::NativeFunction { properties, .. }
6042 | HeapEntry::RegExp { properties, .. }
6043 | HeapEntry::Date { properties, .. }
6044 | HeapEntry::BuiltinIterator { properties, .. }
6045 | HeapEntry::Collection { properties, .. }
6046 | HeapEntry::Promise { properties, .. }
6047 | HeapEntry::Timeout { properties, .. } => {
6048 properties.insert(
6049 key,
6050 Property::Data {
6051 value,
6052 writable: true,
6053 enumerable: true,
6054 configurable: true,
6055 },
6056 );
6057 Ok(())
6058 }
6059 HeapEntry::Array {
6060 elements,
6061 properties,
6062 length_writable,
6063 ..
6064 } => {
6065 match key {
6066 PropertyKey::Named(name) => {
6067 if let Some(offset) = array_index(&name) {
6068 let offset = offset as usize;
6069 if elements.len() <= offset {
6070 array_set_length(
6071 elements,
6072 properties,
6073 *length_writable,
6074 number_value((offset + 1) as f64),
6075 "set array index",
6076 )?;
6077 }
6078 elements[offset] = value;
6079 } else {
6080 properties.insert(
6081 PropertyKey::Named(name),
6082 Property::Data {
6083 value,
6084 writable: true,
6085 enumerable: true,
6086 configurable: true,
6087 },
6088 );
6089 }
6090 }
6091 identity @ (PropertyKey::Symbol(_) | PropertyKey::Private(_)) => {
6092 properties.insert(
6093 identity,
6094 Property::Data {
6095 value,
6096 writable: true,
6097 enumerable: true,
6098 configurable: true,
6099 },
6100 );
6101 }
6102 }
6103 Ok(())
6104 }
6105 HeapEntry::ProcessEnv { .. } => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6106 operation: "set internal process environment",
6107 })),
6108 _ => unreachable!("primitive and identity entries rejected above"),
6109 }
6110 }
6111
6112 fn define_accessor(
6113 &mut self,
6114 object: Value,
6115 key: PropertyKey,
6116 accessor: Value,
6117 kind: AccessorKind,
6118 ) -> Result<(), EvalFailure> {
6119 match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6120 Some(index) => {
6121 self.charge_heap(key.charge_bytes() + 8)
6122 .map_err(EvalFailure::Runtime)?;
6123 let (properties, extensible) = match &mut self.heap[index] {
6124 HeapEntry::Object {
6125 properties,
6126 extensible,
6127 ..
6128 }
6129 | HeapEntry::Generator {
6130 properties,
6131 extensible,
6132 ..
6133 }
6134 | HeapEntry::Script {
6135 properties,
6136 extensible,
6137 ..
6138 }
6139 | HeapEntry::Array {
6140 properties,
6141 extensible,
6142 ..
6143 }
6144 | HeapEntry::Function {
6145 properties,
6146 extensible,
6147 ..
6148 }
6149 | HeapEntry::NativeFunction {
6150 properties,
6151 extensible,
6152 ..
6153 }
6154 | HeapEntry::RegExp {
6155 properties,
6156 extensible,
6157 ..
6158 }
6159 | HeapEntry::Date {
6160 properties,
6161 extensible,
6162 ..
6163 }
6164 | HeapEntry::BuiltinIterator {
6165 properties,
6166 extensible,
6167 ..
6168 }
6169 | HeapEntry::Collection {
6170 properties,
6171 extensible,
6172 ..
6173 }
6174 | HeapEntry::Promise {
6175 properties,
6176 extensible,
6177 ..
6178 } => (properties, *extensible),
6179 _ => {
6180 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6181 operation: "define accessor on primitive",
6182 }));
6183 }
6184 };
6185 if properties
6186 .get(&key)
6187 .is_some_and(|property| !property.configurable())
6188 || (!properties.contains_key(&key) && !extensible)
6189 {
6190 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6191 operation: "define accessor on non-configurable object",
6192 }));
6193 }
6194 let property = properties.get_mut(&key);
6195 match property {
6196 Some(Property::Accessor { getter, setter, .. }) => match kind {
6197 AccessorKind::Getter => *getter = Some(accessor),
6198 AccessorKind::Setter => *setter = Some(accessor),
6199 },
6200 Some(Property::Data { .. }) | None => {
6201 let (getter, setter) = match kind {
6202 AccessorKind::Getter => (Some(accessor), None),
6203 AccessorKind::Setter => (None, Some(accessor)),
6204 };
6205 properties.insert(
6206 key,
6207 Property::Accessor {
6208 getter,
6209 setter,
6210 enumerable: true,
6211 configurable: true,
6212 },
6213 );
6214 }
6215 }
6216 Ok(())
6217 }
6218 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6219 operation: "define accessor on host object",
6220 })),
6221 }
6222 }
6223
6224 fn delete_property(&mut self, object: Value, key: &PropertyKey) -> Result<bool, EvalFailure> {
6225 match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6226 Some(index) => match &mut self.heap[index] {
6227 HeapEntry::Object { properties, .. }
6228 | HeapEntry::Generator { properties, .. }
6229 | HeapEntry::Script { properties, .. }
6230 | HeapEntry::Function { properties, .. }
6231 | HeapEntry::NativeFunction { properties, .. }
6232 | HeapEntry::RegExp { properties, .. }
6233 | HeapEntry::Date { properties, .. }
6234 | HeapEntry::BuiltinIterator { properties, .. }
6235 | HeapEntry::Collection { properties, .. }
6236 | HeapEntry::Promise { properties, .. }
6237 | HeapEntry::Timeout { properties, .. } => {
6238 if properties
6239 .get(key)
6240 .is_some_and(|property| !property.configurable())
6241 {
6242 return Ok(false);
6243 }
6244 properties.remove(key);
6245 Ok(true)
6246 }
6247 HeapEntry::Array {
6248 elements,
6249 properties,
6250 ..
6251 } => {
6252 if properties
6253 .get(key)
6254 .is_some_and(|property| !property.configurable())
6255 {
6256 return Ok(false);
6257 }
6258 if properties.remove(key).is_some() {
6259 return Ok(true);
6260 }
6261 if let PropertyKey::Named(name) = key {
6262 if name.eq_ascii("length") {
6263 return Ok(false);
6264 }
6265 if let Some(offset) = array_index(name) {
6266 if let Some(element) = elements.get_mut(offset as usize) {
6267 *element = Value::HOLE;
6268 }
6269 return Ok(true);
6270 }
6271 }
6272 Ok(true)
6273 }
6274 HeapEntry::ProcessEnv { .. } => {
6275 let PropertyKey::Named(name) = key else {
6276 return Ok(true);
6277 };
6278 Ok(name
6279 .to_utf8_strict()
6280 .is_ok_and(|name| self.host.delete_env(&name)))
6281 }
6282 HeapEntry::String(_)
6283 | HeapEntry::BigInt(_)
6284 | HeapEntry::Symbol { .. }
6285 | HeapEntry::PrivateName { .. }
6286 | HeapEntry::Iterator { .. }
6287 | HeapEntry::PromiseResolver { .. }
6288 | HeapEntry::PromiseFinally { .. }
6289 | HeapEntry::PromiseAll { .. }
6290 | HeapEntry::AsyncActivation { .. }
6291 | HeapEntry::PromiseAllElement { .. }
6292 | HeapEntry::HashState { .. } => Ok(true),
6293 HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
6294 Ok(false)
6295 }
6296 },
6297 None => Ok(true),
6298 }
6299 }
6300
6301 fn has_property(&mut self, object: Value, key: &PropertyKey) -> Result<bool, EvalFailure> {
6302 match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6303 Some(index) => {
6304 if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
6305 let PropertyKey::Named(name) = key else {
6306 return Ok(false);
6307 };
6308 return Ok(name
6309 .to_utf8_strict()
6310 .is_ok_and(|name| self.host.env(&name).is_some()));
6311 }
6312 if matches!(key, PropertyKey::Private(_)) {
6313 return Ok(self.own_get(index, key).is_some());
6314 }
6315 let mut node = index;
6316 let mut guard = 0;
6317 loop {
6318 if self.own_get(node, key).is_some() {
6319 return Ok(true);
6320 }
6321 match self.prototype_index(node)? {
6322 Some(next) => {
6323 node = next;
6324 guard += 1;
6325 if guard > self.heap.len() + 1 {
6326 return Ok(false);
6327 }
6328 }
6329 None => return Ok(false),
6330 }
6331 }
6332 }
6333 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6334 operation: "in",
6335 })),
6336 }
6337 }
6338
6339 pub(crate) fn array_push(&mut self, array: Value, value: Value) -> Result<(), EvalFailure> {
6342 match self.runtime_slot(array).map_err(EvalFailure::Runtime)? {
6343 Some(index) => {
6344 if !matches!(self.heap[index], HeapEntry::Array { .. }) {
6345 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6346 operation: "push on non-array",
6347 }));
6348 }
6349 self.charge_heap(8).map_err(EvalFailure::Runtime)?;
6350 if let HeapEntry::Array {
6351 elements,
6352 properties,
6353 length_writable,
6354 ..
6355 } = &mut self.heap[index]
6356 {
6357 let offset = elements.len();
6358 array_set_length(
6359 elements,
6360 properties,
6361 *length_writable,
6362 number_value((offset + 1) as f64),
6363 "push beyond non-writable array length",
6364 )?;
6365 elements[offset] = value;
6366 }
6367 Ok(())
6368 }
6369 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6370 operation: "push on non-array",
6371 })),
6372 }
6373 }
6374
6375 fn array_extend(&mut self, array: Value, iterable: Value) -> Result<(), EvalFailure> {
6376 let iterator = self.create_iterator(iterable, IteratorKind::Sync)?;
6377 loop {
6378 let (done, value) = self.iterator_next(iterator)?;
6379 if done {
6380 return Ok(());
6381 }
6382 self.array_push(array, value)?;
6383 }
6384 }
6385
6386 fn object_spread(&mut self, target: Value, source: Value) -> Result<(), EvalFailure> {
6387 let target_index = match self.runtime_slot(target).map_err(EvalFailure::Runtime)? {
6388 Some(index)
6389 if matches!(
6390 self.heap[index],
6391 HeapEntry::Object { .. }
6392 | HeapEntry::Generator { .. }
6393 | HeapEntry::Script { .. }
6394 | HeapEntry::Array { .. }
6395 | HeapEntry::Promise { .. }
6396 ) =>
6397 {
6398 index
6399 }
6400 _ => {
6401 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6402 operation: "object spread target is not an object",
6403 }));
6404 }
6405 };
6406 let keys = self.own_property_keys(source)?;
6407 for key in keys {
6408 if !self.own_property_is_enumerable(source, &key)? {
6409 continue;
6410 }
6411 let value = self.get_property_key(source, &key)?;
6412 self.set_own_data(target_index, key, value)?;
6413 }
6414 Ok(())
6415 }
6416
6417 fn set_prototype(&mut self, object: Value, prototype: Value) -> Result<(), EvalFailure> {
6418 let prototype = match self.runtime_slot(prototype).map_err(EvalFailure::Runtime)? {
6419 Some(_) => Some(prototype),
6420 None => match prototype.decode() {
6421 Some(Decoded::Null) => None,
6422 Some(Decoded::HeapRef(_)) => Some(prototype),
6423 _ => {
6424 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6425 operation: "set prototype to non-object",
6426 }));
6427 }
6428 },
6429 };
6430 match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6431 Some(index) => match &mut self.heap[index] {
6432 HeapEntry::Object {
6433 prototype: slot, ..
6434 }
6435 | HeapEntry::Generator {
6436 prototype: slot, ..
6437 }
6438 | HeapEntry::Script {
6439 prototype: slot, ..
6440 }
6441 | HeapEntry::Array {
6442 prototype: slot, ..
6443 }
6444 | HeapEntry::Function {
6445 prototype: slot, ..
6446 }
6447 | HeapEntry::RegExp {
6448 prototype: slot, ..
6449 }
6450 | HeapEntry::Date {
6451 prototype: slot, ..
6452 }
6453 | HeapEntry::BuiltinIterator {
6454 prototype: slot, ..
6455 }
6456 | HeapEntry::Collection {
6457 prototype: slot, ..
6458 }
6459 | HeapEntry::Promise {
6460 prototype: slot, ..
6461 } => {
6462 *slot = prototype;
6463 Ok(())
6464 }
6465 _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6466 operation: "set prototype on primitive",
6467 })),
6468 },
6469 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6470 operation: "set prototype on host object",
6471 })),
6472 }
6473 }
6474
6475 pub(crate) fn create_generator(
6476 &mut self,
6477 start: GeneratorStart,
6478 ) -> Result<Value, RuntimeErrorKind> {
6479 self.allocate(HeapEntry::Generator {
6480 state: GeneratorState::SuspendedStart(start),
6481 properties: PropertyMap::default(),
6482 prototype: Some(self.intrinsics.builtins.generator_prototype()),
6483 extensible: true,
6484 })
6485 }
6486
6487 fn resume_generator(
6488 &mut self,
6489 generator: Value,
6490 resume_value: Value,
6491 ) -> Result<Value, EvalFailure> {
6492 let state = self.take_generator_state(generator)?;
6493 if matches!(&state, GeneratorState::Completed) {
6494 return self.iterator_result(Value::UNDEFINED, true);
6495 }
6496
6497 let stop_depth = self.frames.len();
6498 let return_to = self.frames.last().map(|frame| ReturnTo {
6499 destination: None,
6500 call_pc: frame.pc,
6501 constructed: None,
6502 });
6503 let prepared = match state {
6504 GeneratorState::SuspendedStart(start) => self
6505 .push_frame(
6506 start.target,
6507 &start.captures,
6508 start.this_value,
6509 start.new_target,
6510 &start.args,
6511 return_to,
6512 )
6513 .map_err(|error| EvalFailure::Runtime(error.kind)),
6514 GeneratorState::Suspended(activation) => {
6515 self.push_resumed_generator_frame(activation, resume_value, return_to)
6516 }
6517 GeneratorState::Executing | GeneratorState::Completed => unreachable!(),
6518 };
6519 if let Err(failure) = prepared {
6520 self.settle_generator_completed(generator)?;
6521 return Err(failure);
6522 }
6523
6524 let resumed = self.run_generator_activation(stop_depth);
6525 match resumed {
6526 Ok(GeneratorResume::Yield { value, activation }) => {
6527 self.settle_generator_yield(generator, value, activation)
6528 }
6529 Ok(GeneratorResume::Return(value)) => {
6530 self.settle_generator_completed(generator)?;
6531 self.iterator_result(value, true)
6532 }
6533 Ok(GeneratorResume::Throw { value, origin }) => {
6534 self.settle_generator_completed(generator)?;
6535 Err(EvalFailure::ThrowValueOrigin { value, origin })
6536 }
6537 Err(failure) => {
6538 self.settle_generator_completed(generator)?;
6539 Err(failure)
6540 }
6541 }
6542 }
6543
6544 fn push_resumed_generator_frame(
6545 &mut self,
6546 activation: SuspendedActivation,
6547 resume_value: Value,
6548 return_to: Option<ReturnTo>,
6549 ) -> Result<(), EvalFailure> {
6550 if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
6551 self.release_suspended_activation_registers(activation.registers.len());
6552 return Err(EvalFailure::Runtime(RuntimeErrorKind::CallDepthExceeded {
6553 limit: self.limits.max_call_depth,
6554 }));
6555 }
6556 let suspend_pc = activation
6557 .resume_token
6558 .checked_sub(1)
6559 .expect("suspended generator token is nonzero") as usize;
6560 let instruction = self.module_code(activation.target.module).functions()
6561 [activation.target.function.get() as usize]
6562 .code()[suspend_pc];
6563 let Instruction::Suspend { dst, resume, .. } = instruction else {
6564 unreachable!("generator resume token names a suspend instruction");
6565 };
6566 let mut frame = Frame {
6567 module: activation.target.module,
6568 function: activation.target.function.get() as usize,
6569 pc: resume.get() as usize,
6570 registers: activation.registers,
6571 return_to,
6572 this_value: activation.this_value,
6573 new_target: activation.new_target,
6574 args: activation.args,
6575 arguments_object: activation.arguments_object,
6576 };
6577 frame.registers[dst.get() as usize] = resume_value;
6578 self.frames.push(frame);
6579 Ok(())
6580 }
6581
6582 fn run_generator_activation(
6583 &mut self,
6584 stop_depth: usize,
6585 ) -> Result<GeneratorResume, EvalFailure> {
6586 self.last_completion = None;
6587 self.pending_generator_resume = None;
6588 self.callback_boundaries.push(stop_depth);
6589 self.generator_boundaries.push(stop_depth);
6590 let result = self.run_loop(stop_depth);
6591 self.generator_boundaries
6592 .pop()
6593 .expect("generator execution owns its suspend boundary");
6594 self.callback_boundaries
6595 .pop()
6596 .expect("generator execution owns its unwind boundary");
6597
6598 match result {
6599 Ok(Some(execution)) => Ok(GeneratorResume::Return(execution.value)),
6600 Ok(None) => {
6601 if let Some(resume) = self.pending_generator_resume.take() {
6602 return Ok(resume);
6603 }
6604 let value = self.last_completion.take().unwrap_or(Value::UNDEFINED);
6605 Ok(GeneratorResume::Return(value))
6606 }
6607 Err(error) => {
6608 self.unwind_frames_to(stop_depth);
6609 match error.kind {
6610 RuntimeErrorKind::UncaughtThrow { value, origin } => {
6611 Ok(GeneratorResume::Throw { value, origin })
6612 }
6613 kind => Err(EvalFailure::Runtime(kind)),
6614 }
6615 }
6616 }
6617 }
6618
6619 pub(crate) fn take_generator_state(
6620 &mut self,
6621 generator: Value,
6622 ) -> Result<GeneratorState, EvalFailure> {
6623 let Some(index) = self.runtime_slot(generator).map_err(EvalFailure::Runtime)? else {
6624 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6625 operation: "Generator.prototype.next called on incompatible receiver",
6626 }));
6627 };
6628 let HeapEntry::Generator { state, .. } = &mut self.heap[index] else {
6629 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6630 operation: "Generator.prototype.next called on incompatible receiver",
6631 }));
6632 };
6633 match std::mem::replace(state, GeneratorState::Executing) {
6634 GeneratorState::Executing => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6635 operation: "generator is already running",
6636 })),
6637 GeneratorState::Completed => {
6638 *state = GeneratorState::Completed;
6639 Ok(GeneratorState::Completed)
6640 }
6641 state => Ok(state),
6642 }
6643 }
6644
6645 pub(crate) fn settle_generator_yield(
6646 &mut self,
6647 generator: Value,
6648 value: Value,
6649 activation: SuspendedActivation,
6650 ) -> Result<Value, EvalFailure> {
6651 let register_count = activation.registers.len();
6652 let result = match self.iterator_result(value, false) {
6653 Ok(result) => result,
6654 Err(failure) => {
6655 self.release_suspended_activation_registers(register_count);
6656 self.replace_executing_generator(generator, GeneratorState::Completed)?;
6657 return Err(failure);
6658 }
6659 };
6660 if let Err(failure) =
6661 self.replace_executing_generator(generator, GeneratorState::Suspended(activation))
6662 {
6663 self.release_suspended_activation_registers(register_count);
6664 return Err(failure);
6665 }
6666 Ok(result)
6667 }
6668
6669 pub(crate) fn settle_generator_completed(
6670 &mut self,
6671 generator: Value,
6672 ) -> Result<(), EvalFailure> {
6673 self.replace_executing_generator(generator, GeneratorState::Completed)
6674 }
6675
6676 fn replace_executing_generator(
6677 &mut self,
6678 generator: Value,
6679 next: GeneratorState,
6680 ) -> Result<(), EvalFailure> {
6681 let Some(index) = self.runtime_slot(generator).map_err(EvalFailure::Runtime)? else {
6682 return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6683 value: generator,
6684 }));
6685 };
6686 let HeapEntry::Generator { state, .. } = &mut self.heap[index] else {
6687 return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6688 value: generator,
6689 }));
6690 };
6691 if !matches!(state, GeneratorState::Executing) {
6692 return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6693 value: generator,
6694 }));
6695 }
6696 *state = next;
6697 Ok(())
6698 }
6699
6700 pub(crate) fn start_async_call(
6705 &mut self,
6706 target: RuntimeFunction,
6707 captures: &[Value],
6708 this_value: Value,
6709 new_target: Value,
6710 arguments: &[Value],
6711 ) -> Result<Value, EvalFailure> {
6712 let promise = self.create_promise()?;
6713 let record = self.create_async_activation(promise)?;
6714 let stop_depth = self.frames.len();
6715 let return_to = self.frames.last().map(|frame| ReturnTo {
6716 destination: None,
6717 call_pc: frame.pc,
6718 constructed: None,
6719 });
6720 self.push_frame(
6721 target, captures, this_value, new_target, arguments, return_to,
6722 )
6723 .map_err(|error| EvalFailure::Runtime(error.kind))?;
6724 let step = self.drive_async_activation(stop_depth, None);
6725 self.settle_async_step(record, promise, step)?;
6726 Ok(promise)
6727 }
6728
6729 fn resume_async(
6734 &mut self,
6735 record: Value,
6736 value: Value,
6737 rejection: Option<ThrowOrigin>,
6738 ) -> Result<(), RuntimeErrorKind> {
6739 let promise = self.async_activation_promise(record)?;
6740 let activation = self.take_async_activation(record)?;
6741 let register_count = activation.registers.len();
6742 if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
6743 self.release_suspended_activation_registers(register_count);
6744 return Err(RuntimeErrorKind::CallDepthExceeded {
6745 limit: self.limits.max_call_depth,
6746 });
6747 }
6748 let suspend_pc = activation
6749 .resume_token
6750 .checked_sub(1)
6751 .expect("suspended async token is nonzero") as usize;
6752 let instruction = self.module_code(activation.target.module).functions()
6753 [activation.target.function.get() as usize]
6754 .code()[suspend_pc];
6755 let Instruction::Suspend { dst, resume, .. } = instruction else {
6756 unreachable!("async resume token names a suspend instruction");
6757 };
6758 let stop_depth = self.frames.len();
6759 let return_to = self.frames.last().map(|frame| ReturnTo {
6760 destination: None,
6761 call_pc: frame.pc,
6762 constructed: None,
6763 });
6764 let mut frame = Frame {
6765 module: activation.target.module,
6766 function: activation.target.function.get() as usize,
6767 pc: resume.get() as usize,
6768 registers: activation.registers,
6769 return_to,
6770 this_value: activation.this_value,
6771 new_target: activation.new_target,
6772 args: activation.args,
6773 arguments_object: activation.arguments_object,
6774 };
6775 let inject = match rejection {
6776 None => {
6777 frame.registers[dst.get() as usize] = value;
6778 None
6779 }
6780 Some(origin) => Some((value, origin, suspend_pc)),
6781 };
6782 self.frames.push(frame);
6783 let step = self.drive_async_activation(stop_depth, inject);
6784 match self.settle_async_step(record, promise, step) {
6785 Ok(()) => Ok(()),
6786 Err(EvalFailure::Runtime(kind)) => Err(kind),
6787 Err(_) => Err(RuntimeErrorKind::InvalidValue { value: record }),
6788 }
6789 }
6790
6791 fn drive_async_activation(
6797 &mut self,
6798 stop_depth: usize,
6799 inject: Option<(Value, ThrowOrigin, usize)>,
6800 ) -> Result<AsyncStep, EvalFailure> {
6801 self.last_completion = None;
6802 self.pending_async_suspend = None;
6803 self.callback_boundaries.push(stop_depth);
6804 self.async_boundaries.push(stop_depth);
6805 let result = match inject {
6806 None => self.run_loop(stop_depth),
6807 Some((value, origin, faulting_pc)) => match self.throw(value, origin, faulting_pc) {
6808 Ok(()) => self.run_loop(stop_depth),
6809 Err(error) => Err(error),
6810 },
6811 };
6812 self.async_boundaries
6813 .pop()
6814 .expect("async execution owns its suspend boundary");
6815 self.callback_boundaries
6816 .pop()
6817 .expect("async execution owns its unwind boundary");
6818 match result {
6819 Ok(Some(execution)) => Ok(AsyncStep::Return(execution.value)),
6820 Ok(None) => {
6821 if let Some((awaited, activation)) = self.pending_async_suspend.take() {
6822 Ok(AsyncStep::Suspend {
6823 awaited,
6824 activation,
6825 })
6826 } else {
6827 Ok(AsyncStep::Return(
6828 self.last_completion.take().unwrap_or(Value::UNDEFINED),
6829 ))
6830 }
6831 }
6832 Err(error) => {
6833 self.unwind_frames_to(stop_depth);
6834 match error.kind {
6835 RuntimeErrorKind::UncaughtThrow { value, origin } => {
6836 Ok(AsyncStep::Throw { value, origin })
6837 }
6838 kind => Err(EvalFailure::Runtime(kind)),
6839 }
6840 }
6841 }
6842 }
6843
6844 fn settle_async_step(
6846 &mut self,
6847 record: Value,
6848 promise: Value,
6849 step: Result<AsyncStep, EvalFailure>,
6850 ) -> Result<(), EvalFailure> {
6851 match step {
6852 Ok(AsyncStep::Suspend {
6853 awaited,
6854 activation,
6855 }) => {
6856 let register_count = activation.registers.len();
6857 let result = self
6858 .store_async_activation(record, activation)
6859 .and_then(|()| self.await_promise(awaited, record));
6860 if result.is_err() {
6861 let released = self
6862 .take_async_activation(record)
6863 .map_or(register_count, |stored| stored.registers.len());
6864 self.release_suspended_activation_registers(released);
6865 }
6866 result
6867 }
6868 Ok(AsyncStep::Return(value)) => self
6869 .resolve_promise(promise, value)
6870 .map_err(EvalFailure::Runtime),
6871 Ok(AsyncStep::Throw { value, origin }) => self
6872 .reject_promise(promise, value, origin)
6873 .map_err(EvalFailure::Runtime),
6874 Err(failure) => Err(failure),
6875 }
6876 }
6877
6878 fn await_promise(&mut self, awaited: Value, record: Value) -> Result<(), EvalFailure> {
6882 let promise = self.promise_resolve(awaited)?;
6883 let index = self
6884 .runtime_slot(promise)
6885 .map_err(EvalFailure::Runtime)?
6886 .ok_or(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6887 value: promise,
6888 }))?;
6889 let settled = match &self.heap[index] {
6890 HeapEntry::Promise {
6891 state: PromiseState::Pending { .. },
6892 ..
6893 } => None,
6894 HeapEntry::Promise {
6895 state: PromiseState::Fulfilled { value },
6896 ..
6897 } => Some((true, *value, ThrowOrigin::Bytecode)),
6898 HeapEntry::Promise {
6899 state: PromiseState::Rejected { reason, origin },
6900 ..
6901 } => Some((false, *reason, *origin)),
6902 _ => {
6903 return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6904 value: promise,
6905 }));
6906 }
6907 };
6908 if let Some((fulfilled, value, origin)) = settled {
6909 self.ensure_microtask_capacity(1)
6910 .map_err(EvalFailure::Runtime)?;
6911 let reaction = if fulfilled {
6912 PromiseReaction::AsyncFulfill { activation: record }
6913 } else {
6914 PromiseReaction::AsyncReject { activation: record }
6915 };
6916 self.microtasks.push_back(MicrotaskJob::Reaction {
6917 reaction,
6918 value,
6919 origin,
6920 });
6921 return Ok(());
6922 }
6923 self.charge_promise_reactions(2)?;
6924 let HeapEntry::Promise {
6925 state:
6926 PromiseState::Pending {
6927 fulfill_reactions,
6928 reject_reactions,
6929 },
6930 ..
6931 } = &mut self.heap[index]
6932 else {
6933 unreachable!("pending Promise state was checked before reaction registration");
6934 };
6935 fulfill_reactions.push(PromiseReaction::AsyncFulfill { activation: record });
6936 reject_reactions.push(PromiseReaction::AsyncReject { activation: record });
6937 Ok(())
6938 }
6939
6940 fn create_async_activation(&mut self, promise: Value) -> Result<Value, EvalFailure> {
6941 self.allocate(HeapEntry::AsyncActivation {
6942 activation: None,
6943 promise,
6944 })
6945 .map_err(EvalFailure::Runtime)
6946 }
6947
6948 fn store_async_activation(
6949 &mut self,
6950 record: Value,
6951 activation: SuspendedActivation,
6952 ) -> Result<(), EvalFailure> {
6953 let index = self
6954 .runtime_slot(record)
6955 .map_err(EvalFailure::Runtime)?
6956 .ok_or(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6957 value: record,
6958 }))?;
6959 let HeapEntry::AsyncActivation {
6960 activation: slot, ..
6961 } = &mut self.heap[index]
6962 else {
6963 return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6964 value: record,
6965 }));
6966 };
6967 *slot = Some(activation);
6968 Ok(())
6969 }
6970
6971 fn take_async_activation(
6974 &mut self,
6975 record: Value,
6976 ) -> Result<SuspendedActivation, RuntimeErrorKind> {
6977 let index = self
6978 .runtime_slot(record)?
6979 .ok_or(RuntimeErrorKind::InvalidValue { value: record })?;
6980 let HeapEntry::AsyncActivation {
6981 activation: slot, ..
6982 } = &mut self.heap[index]
6983 else {
6984 return Err(RuntimeErrorKind::InvalidValue { value: record });
6985 };
6986 slot.take()
6987 .ok_or(RuntimeErrorKind::InvalidValue { value: record })
6988 }
6989
6990 fn async_activation_promise(&self, record: Value) -> Result<Value, RuntimeErrorKind> {
6991 let index = self
6992 .runtime_slot(record)?
6993 .ok_or(RuntimeErrorKind::InvalidValue { value: record })?;
6994 let HeapEntry::AsyncActivation { promise, .. } = &self.heap[index] else {
6995 return Err(RuntimeErrorKind::InvalidValue { value: record });
6996 };
6997 Ok(*promise)
6998 }
6999
7000 pub(crate) fn iterator_result(
7001 &mut self,
7002 value: Value,
7003 done: bool,
7004 ) -> Result<Value, EvalFailure> {
7005 let result = self
7006 .allocate(HeapEntry::Object {
7007 properties: PropertyMap::default(),
7008 prototype: Some(self.intrinsics.object_prototype),
7009 boxed_primitive: None,
7010 extensible: true,
7011 })
7012 .map_err(EvalFailure::Runtime)?;
7013 self.set_data_property(result, "value", value)?;
7014 self.set_data_property(result, "done", Value::boolean(done))?;
7015 Ok(result)
7016 }
7017
7018 fn create_iterator(&mut self, src: Value, kind: IteratorKind) -> Result<Value, EvalFailure> {
7021 if kind == IteratorKind::Keys {
7022 let keys = self.enumerable_keys(src)?;
7023 return self
7024 .allocate(HeapEntry::Iterator {
7025 state: IteratorState::Keys { index: 0, keys },
7026 })
7027 .map_err(EvalFailure::Runtime);
7028 }
7029
7030 let iterator_symbol = self.intrinsics.builtins.symbol_iterator();
7031 let iterator_key = self.to_property_key(iterator_symbol)?;
7032 let method = self.get_property_key(src, &iterator_key)?;
7033 if !self.is_callable(method)? {
7034 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7035 operation: "value is not iterable",
7036 }));
7037 }
7038 let iterator = self.call_value(method, src, &[])?;
7039 if !self.is_object(iterator) {
7040 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7041 operation: "iterator method returned a non-object",
7042 }));
7043 }
7044 let next = self.get_named_property(iterator, "next")?;
7045 self.create_protocol_iterator(iterator, next)
7046 }
7047
7048 pub(crate) fn create_protocol_iterator(
7049 &mut self,
7050 iterator: Value,
7051 next: Value,
7052 ) -> Result<Value, EvalFailure> {
7053 self.allocate(HeapEntry::Iterator {
7054 state: IteratorState::Protocol { iterator, next },
7055 })
7056 .map_err(EvalFailure::Runtime)
7057 }
7058
7059 fn own_property_keys(&self, src: Value) -> Result<Vec<PropertyKey>, EvalFailure> {
7060 match self.runtime_slot(src).map_err(EvalFailure::Runtime)? {
7061 Some(index) => match &self.heap[index] {
7062 HeapEntry::Object { properties, .. }
7063 | HeapEntry::Generator { properties, .. }
7064 | HeapEntry::Script { properties, .. }
7065 | HeapEntry::Function { properties, .. }
7066 | HeapEntry::NativeFunction { properties, .. }
7067 | HeapEntry::RegExp { properties, .. }
7068 | HeapEntry::Date { properties, .. }
7069 | HeapEntry::BuiltinIterator { properties, .. }
7070 | HeapEntry::Collection { properties, .. }
7071 | HeapEntry::Promise { properties, .. }
7072 | HeapEntry::Timeout { properties, .. } => Ok(ordered_property_keys(properties)),
7073 HeapEntry::Array {
7074 elements,
7075 properties,
7076 ..
7077 } => {
7078 let mut indices: Vec<(usize, PropertyKey)> = elements
7079 .iter()
7080 .enumerate()
7081 .filter(|(_, element)| **element != Value::HOLE)
7082 .map(|(offset, _)| {
7083 (
7084 offset,
7085 PropertyKey::Named(EcmaString::from_utf8(&offset.to_string())),
7086 )
7087 })
7088 .collect();
7089 let mut suffix = Vec::new();
7090 for key in ordered_property_keys(properties) {
7091 let Some(offset) = key.as_string().and_then(array_index) else {
7092 suffix.push(key);
7093 continue;
7094 };
7095 let offset = offset as usize;
7096 if elements
7097 .get(offset)
7098 .is_some_and(|element| *element != Value::HOLE)
7099 {
7100 continue;
7101 }
7102 indices.push((offset, key));
7103 }
7104 indices.sort_unstable_by_key(|(offset, _)| *offset);
7105 Ok(indices
7106 .into_iter()
7107 .map(|(_, key)| key)
7108 .chain(suffix)
7109 .collect())
7110 }
7111 HeapEntry::String(text) => Ok((0..text.len_units())
7112 .map(|index| PropertyKey::Named(EcmaString::from_utf8(&index.to_string())))
7113 .collect()),
7114 HeapEntry::ModuleNamespace { module } => {
7115 let mut names: Vec<EcmaString> = self
7116 .program_module(*module)
7117 .exports
7118 .iter()
7119 .map(|export| self.constant_text(*module, export.name).clone())
7120 .collect();
7121 names.sort();
7122 Ok(names.into_iter().map(PropertyKey::Named).collect())
7123 }
7124 HeapEntry::ExternalModuleNamespace { specifier } => Ok(self.registry.external
7125 [specifier]
7126 .exports
7127 .keys()
7128 .cloned()
7129 .map(PropertyKey::Named)
7130 .collect()),
7131 HeapEntry::ProcessEnv { .. }
7132 | HeapEntry::BigInt(_)
7133 | HeapEntry::Symbol { .. }
7134 | HeapEntry::PrivateName { .. }
7135 | HeapEntry::HashState { .. }
7136 | HeapEntry::Iterator { .. }
7137 | HeapEntry::PromiseResolver { .. }
7138 | HeapEntry::PromiseFinally { .. }
7139 | HeapEntry::PromiseAll { .. }
7140 | HeapEntry::AsyncActivation { .. }
7141 | HeapEntry::PromiseAllElement { .. } => Ok(Vec::new()),
7142 },
7143 None => Ok(Vec::new()),
7144 }
7145 }
7146
7147 fn own_property_is_enumerable(
7148 &self,
7149 src: Value,
7150 key: &PropertyKey,
7151 ) -> Result<bool, EvalFailure> {
7152 let Some(index) = self.runtime_slot(src).map_err(EvalFailure::Runtime)? else {
7153 return Ok(false);
7154 };
7155 Ok(match &self.heap[index] {
7156 HeapEntry::Array {
7157 elements,
7158 properties,
7159 ..
7160 } => properties.get(key).map_or_else(
7161 || {
7162 key.as_string().is_some_and(|name| {
7163 array_index(name).is_some_and(|offset| {
7164 elements
7165 .get(offset as usize)
7166 .is_some_and(|element| *element != Value::HOLE)
7167 })
7168 })
7169 },
7170 Property::enumerable,
7171 ),
7172 HeapEntry::String(text) => key.as_string().is_some_and(|name| {
7173 array_index(name).is_some_and(|offset| (offset as usize) < text.len_units())
7174 }),
7175 HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
7176 matches!(key, PropertyKey::Named(_))
7177 }
7178 HeapEntry::Object { properties, .. }
7179 | HeapEntry::Generator { properties, .. }
7180 | HeapEntry::Script { properties, .. }
7181 | HeapEntry::Function { properties, .. }
7182 | HeapEntry::NativeFunction { properties, .. }
7183 | HeapEntry::RegExp { properties, .. }
7184 | HeapEntry::Date { properties, .. }
7185 | HeapEntry::BuiltinIterator { properties, .. }
7186 | HeapEntry::Collection { properties, .. }
7187 | HeapEntry::Promise { properties, .. }
7188 | HeapEntry::Timeout { properties, .. } => {
7189 properties.get(key).is_some_and(Property::enumerable)
7190 }
7191 _ => false,
7192 })
7193 }
7194
7195 fn enumerable_keys(&self, src: Value) -> Result<Vec<EcmaString>, EvalFailure> {
7196 let mut names = Vec::new();
7197 for key in self.own_property_keys(src)? {
7198 if !self.own_property_is_enumerable(src, &key)? {
7199 continue;
7200 }
7201 if let PropertyKey::Named(name) = key {
7202 names.push(name);
7203 }
7204 }
7205 Ok(names)
7206 }
7207
7208 fn iterator_next(&mut self, iterator: Value) -> Result<(bool, Value), EvalFailure> {
7209 let (callee, this_value) = match self.prepare_iterator_next(iterator)? {
7210 IteratorNextPrepared::Ready { done, value } => return Ok((done, value)),
7211 IteratorNextPrepared::Call { callee, this_value } => (callee, this_value),
7212 };
7213
7214 let result = self.call_value(callee, this_value, &[])?;
7215 if !self.is_object(result) {
7216 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7217 operation: "iterator next returned a non-object",
7218 }));
7219 }
7220 let done = self.get_named_property(result, "done")?;
7221 if self.truthy(done) {
7222 return Ok((true, Value::UNDEFINED));
7223 }
7224 let value = self.get_named_property(result, "value")?;
7225 Ok((false, value))
7226 }
7227
7228 pub(crate) fn prepare_iterator_next(
7229 &mut self,
7230 iterator: Value,
7231 ) -> Result<IteratorNextPrepared, EvalFailure> {
7232 let iterator_index = self
7233 .runtime_slot(iterator)
7234 .map_err(EvalFailure::Runtime)?
7235 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
7236 operation: "iterator next on non-iterator",
7237 }))?;
7238 match &self.heap[iterator_index] {
7239 HeapEntry::Iterator {
7240 state: IteratorState::Keys { index, keys },
7241 } => {
7242 let Some(text) = keys.get(*index).cloned() else {
7243 return Ok(IteratorNextPrepared::Ready {
7244 done: true,
7245 value: Value::UNDEFINED,
7246 });
7247 };
7248 let value = self
7249 .allocate(HeapEntry::String(text))
7250 .map_err(EvalFailure::Runtime)?;
7251 self.advance_iterator(iterator_index);
7252 Ok(IteratorNextPrepared::Ready { done: false, value })
7253 }
7254 HeapEntry::Iterator {
7255 state: IteratorState::Protocol { iterator, next },
7256 } => Ok(IteratorNextPrepared::Call {
7257 callee: *next,
7258 this_value: *iterator,
7259 }),
7260 _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7261 operation: "iterator next on non-iterator",
7262 })),
7263 }
7264 }
7265
7266 pub(crate) fn iterable_values(&mut self, source: Value) -> Result<Vec<Value>, EvalFailure> {
7267 let iterator = self.create_iterator(source, IteratorKind::Sync)?;
7268 let mut values = Vec::new();
7269 loop {
7270 let (done, value) = self.iterator_next(iterator)?;
7271 if done {
7272 return Ok(values);
7273 }
7274 let bytes = values
7275 .len()
7276 .checked_add(1)
7277 .and_then(|length| length.checked_mul(std::mem::size_of::<Value>()))
7278 .ok_or(EvalFailure::Runtime(
7279 RuntimeErrorKind::HeapByteLimitExceeded {
7280 limit: self.limits.max_heap_bytes,
7281 },
7282 ))?;
7283 self.ensure_allocation_capacity(1, bytes)
7284 .map_err(EvalFailure::Runtime)?;
7285 values.push(value);
7286 }
7287 }
7288
7289 fn advance_iterator(&mut self, iterator_index: usize) {
7290 if let HeapEntry::Iterator {
7291 state: IteratorState::Keys { index, .. },
7292 } = &mut self.heap[iterator_index]
7293 {
7294 *index += 1;
7295 }
7296 }
7297
7298 fn eval_unary(&mut self, op: UnaryOp, operand: Value) -> Result<Value, EvalFailure> {
7301 match op {
7302 UnaryOp::Void => Ok(Value::UNDEFINED),
7303 UnaryOp::TypeOf => {
7304 let text = EcmaString::from_utf8(self.type_of(operand));
7305 self.allocate(HeapEntry::String(text))
7306 .map_err(EvalFailure::Runtime)
7307 }
7308 UnaryOp::Plus => self.to_number(operand),
7309 UnaryOp::Negate => {
7310 if let Some(text) = self.bigint_text(operand) {
7311 let negated = if text == "0" {
7312 "0".to_owned()
7313 } else if let Some(positive) = text.strip_prefix('-') {
7314 positive.to_owned()
7315 } else {
7316 format!("-{text}")
7317 };
7318 return self
7319 .allocate(HeapEntry::BigInt(negated))
7320 .map_err(EvalFailure::Runtime);
7321 }
7322 let number =
7323 numeric_f64(self.to_number(operand)?).expect("ToNumber returns numeric");
7324 Ok(number_value(-number))
7325 }
7326 UnaryOp::BitwiseNot => {
7327 if let Some(text) = self.bigint_text(operand) {
7328 let value = text.parse::<i128>().map_err(|_| {
7329 EvalFailure::Throw(ThrowOrigin::RangeError {
7330 operation: "bigint bitwise not",
7331 })
7332 })?;
7333 return self
7334 .allocate(HeapEntry::BigInt((!value).to_string()))
7335 .map_err(EvalFailure::Runtime);
7336 }
7337 Ok(Value::int32(
7338 (!to_int32(numeric_f64(self.to_number(operand)?).unwrap())) as u32,
7339 ))
7340 }
7341 UnaryOp::LogicalNot => Ok(Value::boolean(!self.truthy(operand))),
7342 }
7343 }
7344
7345 fn eval_binary(
7346 &mut self,
7347 op: BinaryOp,
7348 left: Value,
7349 right: Value,
7350 ) -> Result<Value, EvalFailure> {
7351 match op {
7352 BinaryOp::StrictEqual => Ok(Value::boolean(self.strict_equal(left, right))),
7353 BinaryOp::StrictNotEqual => Ok(Value::boolean(!self.strict_equal(left, right))),
7354 BinaryOp::Equal | BinaryOp::NotEqual => {
7355 let equal = self.abstract_equal(left, right)?;
7356 Ok(Value::boolean(if op == BinaryOp::Equal {
7357 equal
7358 } else {
7359 !equal
7360 }))
7361 }
7362 BinaryOp::LessThan
7363 | BinaryOp::LessThanOrEqual
7364 | BinaryOp::GreaterThan
7365 | BinaryOp::GreaterThanOrEqual => {
7366 let ordering = self.relational_compare(left, right)?;
7367 let result = match (op, ordering) {
7368 (_, None) => false,
7369 (BinaryOp::LessThan, Some(order)) => order == Ordering::Less,
7370 (BinaryOp::LessThanOrEqual, Some(order)) => order != Ordering::Greater,
7371 (BinaryOp::GreaterThan, Some(order)) => order == Ordering::Greater,
7372 (BinaryOp::GreaterThanOrEqual, Some(order)) => order != Ordering::Less,
7373 _ => unreachable!(),
7374 };
7375 Ok(Value::boolean(result))
7376 }
7377 BinaryOp::InstanceOf => self.instance_of(left, right).map(Value::boolean),
7378 BinaryOp::In => {
7379 let key = self.to_property_key(left)?;
7380 self.has_property(right, &key).map(Value::boolean)
7381 }
7382 BinaryOp::Add => self.add(left, right),
7383 BinaryOp::Subtract
7384 | BinaryOp::Multiply
7385 | BinaryOp::Divide
7386 | BinaryOp::Remainder
7387 | BinaryOp::Exponent
7388 | BinaryOp::BitAnd
7389 | BinaryOp::BitOr
7390 | BinaryOp::BitXor
7391 | BinaryOp::ShiftLeft
7392 | BinaryOp::ShiftRight
7393 | BinaryOp::UnsignedShiftRight => self.numeric_binary(op, left, right),
7394 }
7395 }
7396
7397 fn add(&mut self, left: Value, right: Value) -> Result<Value, EvalFailure> {
7398 let left = self.to_primitive_default(left)?;
7399 let right = self.to_primitive_default(right)?;
7400 let left_string = self.string_text(left).cloned();
7401 let right_string = self.string_text(right).cloned();
7402 if left_string.is_some() || right_string.is_some() {
7403 let left = match left_string {
7404 Some(text) => text,
7405 None => self.to_string(left)?,
7406 };
7407 let right = match right_string {
7408 Some(text) => text,
7409 None => self.to_string(right)?,
7410 };
7411 let mut builder = EcmaStringBuilder::with_capacity(
7412 left.len_units().saturating_add(right.len_units()),
7413 );
7414 for &unit in left.as_units() {
7415 builder.push_unit(unit);
7416 }
7417 for &unit in right.as_units() {
7418 builder.push_unit(unit);
7419 }
7420 return self
7421 .allocate(HeapEntry::String(builder.finish()))
7422 .map_err(EvalFailure::Runtime);
7423 }
7424 let left_bigint = self.bigint_text(left).map(str::to_owned);
7425 let right_bigint = self.bigint_text(right).map(str::to_owned);
7426 match (left_bigint, right_bigint) {
7427 (Some(left), Some(right)) => {
7428 let sum = bigint_i128(&left)?
7429 .checked_add(bigint_i128(&right)?)
7430 .ok_or(EvalFailure::Throw(ThrowOrigin::RangeError {
7431 operation: "bigint add overflow",
7432 }))?;
7433 return self
7434 .allocate(HeapEntry::BigInt(sum.to_string()))
7435 .map_err(EvalFailure::Runtime);
7436 }
7437 (Some(_), None) | (None, Some(_)) => {
7438 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7439 operation: "add bigint and number",
7440 }));
7441 }
7442 (None, None) => {}
7443 }
7444 let left = numeric_f64(self.to_number(left)?).unwrap();
7445 let right = numeric_f64(self.to_number(right)?).unwrap();
7446 Ok(number_value(left + right))
7447 }
7448
7449 fn numeric_binary(
7450 &mut self,
7451 op: BinaryOp,
7452 left: Value,
7453 right: Value,
7454 ) -> Result<Value, EvalFailure> {
7455 let left_bigint = self.bigint_text(left).map(str::to_owned);
7456 let right_bigint = self.bigint_text(right).map(str::to_owned);
7457 if left_bigint.is_some() || right_bigint.is_some() {
7458 let (Some(left), Some(right)) = (left_bigint, right_bigint) else {
7459 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7460 operation: "mix bigint and number",
7461 }));
7462 };
7463 let result = bigint_binary(op, &left, &right)?;
7464 return self
7465 .allocate(HeapEntry::BigInt(result))
7466 .map_err(EvalFailure::Runtime);
7467 }
7468 let left = numeric_f64(self.to_number(left)?).unwrap();
7469 let right = numeric_f64(self.to_number(right)?).unwrap();
7470 let value = match op {
7471 BinaryOp::Subtract => number_value(left - right),
7472 BinaryOp::Multiply => number_value(left * right),
7473 BinaryOp::Divide => Value::number(left / right),
7474 BinaryOp::Remainder => Value::number(left % right),
7475 BinaryOp::Exponent => Value::number(left.powf(right)),
7476 BinaryOp::BitAnd => Value::int32((to_int32(left) & to_int32(right)) as u32),
7477 BinaryOp::BitOr => Value::int32((to_int32(left) | to_int32(right)) as u32),
7478 BinaryOp::BitXor => Value::int32((to_int32(left) ^ to_int32(right)) as u32),
7479 BinaryOp::ShiftLeft => {
7480 Value::int32(to_int32(left).wrapping_shl(to_uint32(right) & 31) as u32)
7481 }
7482 BinaryOp::ShiftRight => {
7483 Value::int32((to_int32(left) >> (to_uint32(right) & 31)) as u32)
7484 }
7485 BinaryOp::UnsignedShiftRight => {
7486 number_value((to_uint32(left) >> (to_uint32(right) & 31)) as f64)
7487 }
7488 _ => unreachable!("numeric binary operator partition"),
7489 };
7490 Ok(value)
7491 }
7492
7493 fn coercion_is_primitive(&self, value: Value) -> Result<bool, EvalFailure> {
7494 let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
7495 return Ok(true);
7496 };
7497 Ok(matches!(
7498 self.heap[index],
7499 HeapEntry::String(_)
7500 | HeapEntry::BigInt(_)
7501 | HeapEntry::Symbol { .. }
7502 | HeapEntry::PrivateName { .. }
7503 ))
7504 }
7505
7506 fn to_primitive_default(&mut self, value: Value) -> Result<Value, EvalFailure> {
7507 let prefer_string = self
7508 .runtime_slot(value)
7509 .map_err(EvalFailure::Runtime)?
7510 .is_some_and(|index| matches!(self.heap[index], HeapEntry::Date { .. }));
7511 self.to_primitive_observable(value, prefer_string)
7512 }
7513
7514 pub(crate) fn to_primitive_observable(
7515 &mut self,
7516 value: Value,
7517 prefer_string: bool,
7518 ) -> Result<Value, EvalFailure> {
7519 if self.coercion_is_primitive(value)? {
7520 return Ok(value);
7521 }
7522 let methods = if prefer_string {
7523 ["toString", "valueOf"]
7524 } else {
7525 ["valueOf", "toString"]
7526 };
7527 for name in methods {
7528 let method = self.get_named_property(value, name)?;
7529 if !self.is_callable(method)? {
7530 continue;
7531 }
7532 let primitive = self.call_value(method, value, &[])?;
7533 if self.coercion_is_primitive(primitive)? {
7534 return Ok(primitive);
7535 }
7536 }
7537 Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7538 operation: "cannot convert object to primitive",
7539 }))
7540 }
7541
7542 pub(crate) fn to_string_observable(&mut self, value: Value) -> Result<EcmaString, EvalFailure> {
7543 let primitive = self.to_primitive_observable(value, true)?;
7544 self.to_string(primitive)
7545 }
7546
7547 pub(crate) fn to_number_observable(&mut self, value: Value) -> Result<Value, EvalFailure> {
7548 let primitive = self.to_primitive_observable(value, false)?;
7549 self.to_number(primitive)
7550 }
7551
7552 fn to_number(&self, value: Value) -> Result<Value, EvalFailure> {
7553 match value.decode() {
7554 Some(Decoded::Number(_)) | Some(Decoded::Int32(_)) => self.to_primitive(value),
7555 Some(Decoded::Undefined) => Ok(Value::number(f64::NAN)),
7556 Some(Decoded::Null) => Ok(Value::int32(0)),
7557 Some(Decoded::Boolean(value)) => Ok(Value::int32(u32::from(value))),
7558 Some(Decoded::Hole) | Some(Decoded::Uninitialized) => Ok(Value::number(f64::NAN)),
7559 Some(Decoded::HeapRef(_)) => {
7560 match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7561 Some(index) => match &self.heap[index] {
7562 HeapEntry::String(text) => Ok(number_value(parse_number(text))),
7563 HeapEntry::BigInt(_) => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7564 operation: "convert bigint to number",
7565 })),
7566 HeapEntry::Array { elements, .. } if elements.is_empty() => {
7567 Ok(Value::int32(0))
7568 }
7569 HeapEntry::Array { elements, .. } if elements.len() == 1 => {
7570 self.to_number(elements[0])
7571 }
7572 HeapEntry::Symbol { .. } | HeapEntry::PrivateName { .. } => {
7573 Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7574 operation: "convert symbol to number",
7575 }))
7576 }
7577 HeapEntry::Object { .. }
7578 | HeapEntry::Generator { .. }
7579 | HeapEntry::Script { .. }
7580 | HeapEntry::Array { .. }
7581 | HeapEntry::Function { .. }
7582 | HeapEntry::ModuleNamespace { .. }
7583 | HeapEntry::ExternalModuleNamespace { .. }
7584 | HeapEntry::HashState { .. }
7585 | HeapEntry::NativeFunction { .. }
7586 | HeapEntry::RegExp { .. }
7587 | HeapEntry::Date { .. }
7588 | HeapEntry::BuiltinIterator { .. }
7589 | HeapEntry::Collection { .. }
7590 | HeapEntry::Promise { .. }
7591 | HeapEntry::PromiseResolver { .. }
7592 | HeapEntry::PromiseFinally { .. }
7593 | HeapEntry::PromiseAll { .. }
7594 | HeapEntry::AsyncActivation { .. }
7595 | HeapEntry::PromiseAllElement { .. }
7596 | HeapEntry::ProcessEnv { .. }
7597 | HeapEntry::Iterator { .. }
7598 | HeapEntry::Timeout { .. } => Ok(Value::number(f64::NAN)),
7599 },
7600 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7601 operation: "coerce host object to number",
7602 })),
7603 }
7604 }
7605 None => Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
7606 value,
7607 })),
7608 }
7609 }
7610
7611 fn truthy(&self, value: Value) -> bool {
7612 match value.decode() {
7613 Some(Decoded::Number(number)) => number != 0.0 && !number.is_nan(),
7614 Some(Decoded::Int32(value)) => value != 0,
7615 Some(Decoded::Undefined | Decoded::Null | Decoded::Hole | Decoded::Uninitialized)
7616 | None => false,
7617 Some(Decoded::Boolean(value)) => value,
7618 Some(Decoded::HeapRef(_)) => match self.runtime_slot(value) {
7619 Ok(Some(index)) => match &self.heap[index] {
7620 HeapEntry::String(text) => !text.is_empty(),
7621 HeapEntry::BigInt(text) => text != "0",
7622 HeapEntry::Object { .. }
7623 | HeapEntry::Generator { .. }
7624 | HeapEntry::Script { .. }
7625 | HeapEntry::Array { .. }
7626 | HeapEntry::Function { .. }
7627 | HeapEntry::ModuleNamespace { .. }
7628 | HeapEntry::ExternalModuleNamespace { .. }
7629 | HeapEntry::HashState { .. }
7630 | HeapEntry::NativeFunction { .. }
7631 | HeapEntry::Symbol { .. }
7632 | HeapEntry::PrivateName { .. }
7633 | HeapEntry::RegExp { .. }
7634 | HeapEntry::Date { .. }
7635 | HeapEntry::BuiltinIterator { .. }
7636 | HeapEntry::Collection { .. }
7637 | HeapEntry::Promise { .. }
7638 | HeapEntry::PromiseResolver { .. }
7639 | HeapEntry::PromiseFinally { .. }
7640 | HeapEntry::PromiseAll { .. }
7641 | HeapEntry::AsyncActivation { .. }
7642 | HeapEntry::PromiseAllElement { .. }
7643 | HeapEntry::ProcessEnv { .. }
7644 | HeapEntry::Iterator { .. }
7645 | HeapEntry::Timeout { .. } => true,
7646 },
7647 Ok(None) => true,
7648 Err(_) => false,
7649 },
7650 }
7651 }
7652
7653 fn type_of(&self, value: Value) -> &'static str {
7654 match value.decode() {
7655 Some(Decoded::Undefined | Decoded::Hole | Decoded::Uninitialized) | None => "undefined",
7656 Some(Decoded::Number(_) | Decoded::Int32(_)) => "number",
7657 Some(Decoded::Null) => "object",
7658 Some(Decoded::Boolean(_)) => "boolean",
7659 Some(Decoded::HeapRef(_)) => match self.runtime_slot(value) {
7660 Ok(Some(index)) => match &self.heap[index] {
7661 HeapEntry::String(_) => "string",
7662 HeapEntry::BigInt(_) => "bigint",
7663 HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. } => "function",
7664 HeapEntry::Symbol { .. } => "symbol",
7665 HeapEntry::PrivateName { .. } => "object",
7666 HeapEntry::Object { .. }
7667 | HeapEntry::Generator { .. }
7668 | HeapEntry::Script { .. }
7669 | HeapEntry::Array { .. }
7670 | HeapEntry::ModuleNamespace { .. }
7671 | HeapEntry::ExternalModuleNamespace { .. }
7672 | HeapEntry::HashState { .. }
7673 | HeapEntry::RegExp { .. }
7674 | HeapEntry::Date { .. }
7675 | HeapEntry::BuiltinIterator { .. }
7676 | HeapEntry::Collection { .. }
7677 | HeapEntry::Promise { .. }
7678 | HeapEntry::PromiseResolver { .. }
7679 | HeapEntry::PromiseFinally { .. }
7680 | HeapEntry::PromiseAll { .. }
7681 | HeapEntry::AsyncActivation { .. }
7682 | HeapEntry::PromiseAllElement { .. }
7683 | HeapEntry::ProcessEnv { .. }
7684 | HeapEntry::Iterator { .. }
7685 | HeapEntry::Timeout { .. } => "object",
7686 },
7687 _ => "object",
7688 },
7689 }
7690 }
7691
7692 fn strict_equal(&self, left: Value, right: Value) -> bool {
7693 match (left.decode(), right.decode()) {
7694 (Some(Decoded::Number(a)), Some(Decoded::Number(b))) => a == b,
7695 (Some(Decoded::Number(a)), Some(Decoded::Int32(b)))
7696 | (Some(Decoded::Int32(b)), Some(Decoded::Number(a))) => a == f64::from(b as i32),
7697 (Some(Decoded::Int32(a)), Some(Decoded::Int32(b))) => a == b,
7698 (Some(Decoded::HeapRef(_)), Some(Decoded::HeapRef(_))) => {
7699 match (self.runtime_slot(left), self.runtime_slot(right)) {
7700 (Ok(Some(a)), Ok(Some(b))) => match (&self.heap[a], &self.heap[b]) {
7701 (HeapEntry::String(a), HeapEntry::String(b)) => a == b,
7702 (HeapEntry::BigInt(a), HeapEntry::BigInt(b)) => a == b,
7703 _ => left == right,
7704 },
7705 _ => left == right,
7706 }
7707 }
7708 _ => left == right,
7709 }
7710 }
7711
7712 fn abstract_equal(&self, left: Value, right: Value) -> Result<bool, EvalFailure> {
7713 if self.strict_equal(left, right) {
7714 return Ok(true);
7715 }
7716 if matches!(
7717 (left.decode(), right.decode()),
7718 (Some(Decoded::Null), Some(Decoded::Undefined))
7719 | (Some(Decoded::Undefined), Some(Decoded::Null))
7720 ) {
7721 return Ok(true);
7722 }
7723 let left_number = self.to_number(left);
7724 let right_number = self.to_number(right);
7725 match (left_number, right_number) {
7726 (Ok(left), Ok(right)) => Ok(numeric_f64(left).unwrap() == numeric_f64(right).unwrap()),
7727 _ => Ok(false),
7728 }
7729 }
7730
7731 fn relational_compare(
7732 &self,
7733 left: Value,
7734 right: Value,
7735 ) -> Result<Option<Ordering>, EvalFailure> {
7736 if let (Some(left), Some(right)) = (self.string_text(left), self.string_text(right)) {
7737 return Ok(Some(left.cmp(right)));
7738 }
7739 if let (Some(left), Some(right)) = (self.bigint_text(left), self.bigint_text(right)) {
7740 return Ok(Some(bigint_i128(left)?.cmp(&bigint_i128(right)?)));
7741 }
7742 let left = numeric_f64(self.to_number(left)?).unwrap();
7743 let right = numeric_f64(self.to_number(right)?).unwrap();
7744 Ok(left.partial_cmp(&right))
7745 }
7746
7747 fn instance_of(&mut self, value: Value, constructor: Value) -> Result<bool, EvalFailure> {
7750 let constructor = self
7751 .bound_target(constructor)
7752 .map_err(EvalFailure::Runtime)?;
7753 match self
7754 .runtime_slot(constructor)
7755 .map_err(EvalFailure::Runtime)?
7756 {
7757 Some(index) => {
7758 if !matches!(
7759 self.heap[index],
7760 HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. }
7761 ) {
7762 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7763 operation: "instanceof",
7764 }));
7765 }
7766 let target = match self.own_get_ascii(index, "prototype") {
7767 Some(Found::Value(value)) if self.is_object(value) => value,
7768 _ => {
7769 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7770 operation: "instanceof prototype is not an object",
7771 }));
7772 }
7773 };
7774 let target_slot = self.runtime_slot(target).map_err(EvalFailure::Runtime)?;
7775 let mut node = match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7776 Some(node) => node,
7777 None => return Ok(false),
7778 };
7779 let mut guard = 0;
7780 loop {
7781 if Some(node) == target_slot {
7782 return Ok(true);
7783 }
7784 match self.prototype_index(node)? {
7785 Some(next) => {
7786 node = next;
7787 guard += 1;
7788 if guard > self.heap.len() + 1 {
7789 return Ok(false);
7790 }
7791 }
7792 None => return Ok(false),
7793 }
7794 }
7795 }
7796 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7797 operation: "instanceof",
7798 })),
7799 }
7800 }
7801
7802 fn value_to_string(&self, value: Value, depth: usize) -> Result<EcmaString, EvalFailure> {
7803 if depth >= 32 {
7804 return Ok(EcmaString::default());
7805 }
7806 let ascii = |text: String| EcmaString::from_utf8(&text);
7807 match value.decode() {
7808 Some(Decoded::Number(number)) => Ok(ascii(Self::ordinary_number_to_string(number))),
7809 Some(Decoded::Int32(raw)) => Ok(ascii((raw as i32).to_string())),
7810 Some(Decoded::Undefined | Decoded::Uninitialized) => {
7811 Ok(EcmaString::from_utf8("undefined"))
7812 }
7813 Some(Decoded::Null) => Ok(EcmaString::from_utf8("null")),
7814 Some(Decoded::Boolean(value)) => {
7815 Ok(EcmaString::from_utf8(if value { "true" } else { "false" }))
7816 }
7817 Some(Decoded::Hole) => Ok(EcmaString::default()),
7818 Some(Decoded::HeapRef(_)) => {
7819 match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7820 Some(index) => match &self.heap[index] {
7821 HeapEntry::String(text) => Ok(text.clone()),
7822 HeapEntry::BigInt(text) => Ok(EcmaString::from_utf8(text)),
7823 HeapEntry::Object { .. }
7824 | HeapEntry::Generator { .. }
7825 | HeapEntry::Script { .. }
7826 | HeapEntry::Date { .. }
7827 | HeapEntry::BuiltinIterator { .. }
7828 | HeapEntry::Collection { .. }
7829 | HeapEntry::Promise { .. }
7830 | HeapEntry::PromiseResolver { .. }
7831 | HeapEntry::PromiseFinally { .. }
7832 | HeapEntry::PromiseAll { .. }
7833 | HeapEntry::AsyncActivation { .. }
7834 | HeapEntry::PromiseAllElement { .. }
7835 | HeapEntry::ModuleNamespace { .. }
7836 | HeapEntry::ExternalModuleNamespace { .. }
7837 | HeapEntry::ProcessEnv { .. }
7838 | HeapEntry::Iterator { .. }
7839 | HeapEntry::Timeout { .. }
7840 | HeapEntry::HashState { .. } => {
7841 Ok(EcmaString::from_utf8("[object Object]"))
7842 }
7843 HeapEntry::RegExp { pattern, flags, .. } => {
7844 let mut builder = EcmaStringBuilder::with_capacity(
7845 pattern
7846 .len_units()
7847 .saturating_add(flags.len_units())
7848 .saturating_add(2),
7849 );
7850 builder.push_unit(u16::from(b'/'));
7851 for &unit in pattern.as_units() {
7852 builder.push_unit(unit);
7853 }
7854 builder.push_unit(u16::from(b'/'));
7855 for &unit in flags.as_units() {
7856 builder.push_unit(unit);
7857 }
7858 Ok(builder.finish())
7859 }
7860 HeapEntry::Symbol { .. } => {
7861 Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7862 operation: "convert symbol to string",
7863 }))
7864 }
7865 HeapEntry::PrivateName { .. } => {
7866 Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7867 operation: "convert private name to string",
7868 }))
7869 }
7870 HeapEntry::Function {
7871 module, function, ..
7872 } => {
7873 let flags = self.module_code(*module).functions()
7874 [function.get() as usize]
7875 .flags();
7876 Ok(EcmaString::from_utf8(
7877 match (flags.is_async, flags.is_generator) {
7878 (true, true) => "async function* () { [bytecode] }",
7879 (true, false) => "async function () { [bytecode] }",
7880 (false, true) => "function* () { [bytecode] }",
7881 (false, false) => "function () { [bytecode] }",
7882 },
7883 ))
7884 }
7885 HeapEntry::NativeFunction { .. } => {
7886 Ok(EcmaString::from_utf8("function () { [native code] }"))
7887 }
7888 HeapEntry::Array { elements, .. } => {
7889 let mut text = EcmaStringBuilder::new();
7890 for (index, element) in elements.iter().copied().enumerate() {
7891 if index != 0 {
7892 text.push_unit(u16::from(b','));
7893 }
7894 if element != Value::HOLE
7895 && element != Value::NULL
7896 && element != Value::UNDEFINED
7897 {
7898 for &unit in
7899 self.value_to_string(element, depth + 1)?.as_units()
7900 {
7901 text.push_unit(unit);
7902 }
7903 }
7904 }
7905 Ok(text.finish())
7906 }
7907 },
7908 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7909 operation: "coerce host object to string",
7910 })),
7911 }
7912 }
7913 None => Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
7914 value,
7915 })),
7916 }
7917 }
7918
7919 fn string_text(&self, value: Value) -> Option<&EcmaString> {
7920 let index = self.runtime_slot(value).ok()??;
7921 match &self.heap[index] {
7922 HeapEntry::String(text) => Some(text),
7923 _ => None,
7924 }
7925 }
7926
7927 fn bigint_text(&self, value: Value) -> Option<&str> {
7928 let index = self.runtime_slot(value).ok()??;
7929 match &self.heap[index] {
7930 HeapEntry::BigInt(text) => Some(text),
7931 _ => None,
7932 }
7933 }
7934
7935 fn is_object(&self, value: Value) -> bool {
7936 match self.runtime_slot(value) {
7937 Ok(Some(index)) => !matches!(
7938 self.heap[index],
7939 HeapEntry::String(_)
7940 | HeapEntry::BigInt(_)
7941 | HeapEntry::PromiseResolver { .. }
7942 | HeapEntry::PromiseFinally { .. }
7943 | HeapEntry::PromiseAll { .. }
7944 | HeapEntry::AsyncActivation { .. }
7945 | HeapEntry::PromiseAllElement { .. }
7946 ),
7947 Ok(None) => matches!(value.decode(), Some(Decoded::HeapRef(_))),
7948 Err(_) => false,
7949 }
7950 }
7951}
7952
7953fn ordered_property_keys(properties: &PropertyMap) -> Vec<PropertyKey> {
7954 let mut indices = Vec::new();
7955 let mut strings = Vec::new();
7956 let mut symbols = Vec::new();
7957 for (key, _) in properties.iter() {
7958 match key {
7959 PropertyKey::Named(name) => match array_index(name) {
7960 Some(index) => indices.push((index, key.clone())),
7961 None => strings.push(key.clone()),
7962 },
7963 PropertyKey::Symbol(_) => symbols.push(key.clone()),
7964 PropertyKey::Private(_) => {}
7965 }
7966 }
7967 indices.sort_unstable_by_key(|(index, _)| *index);
7968 indices
7969 .into_iter()
7970 .map(|(_, key)| key)
7971 .chain(strings)
7972 .chain(symbols)
7973 .collect()
7974}
7975
7976fn property_lookup(properties: &PropertyMap, key: &PropertyKey) -> Option<Found> {
7977 match properties.get(key) {
7978 Some(Property::Data { value, .. }) => Some(Found::Value(*value)),
7979 Some(Property::Accessor { getter, .. }) => Some(match getter {
7980 Some(getter) => Found::Getter(*getter),
7981 None => Found::NoGetter,
7982 }),
7983 None => None,
7984 }
7985}
7986
7987fn property_lookup_ascii(properties: &PropertyMap, name: &str) -> Option<Found> {
7988 match properties.get_ascii(name) {
7989 Some(Property::Data { value, .. }) => Some(Found::Value(*value)),
7990 Some(Property::Accessor { getter, .. }) => Some(match getter {
7991 Some(getter) => Found::Getter(*getter),
7992 None => Found::NoGetter,
7993 }),
7994 None => None,
7995 }
7996}
7997
7998fn innermost_handler(function: &Function, pc: usize) -> Option<bamts_bytecode::ExceptionHandler> {
7999 function
8000 .handlers()
8001 .iter()
8002 .copied()
8003 .filter(|handler| handler.start.get() as usize <= pc && pc < handler.end.get() as usize)
8004 .max_by(|left, right| {
8005 left.start
8006 .get()
8007 .cmp(&right.start.get())
8008 .then_with(|| right.end.get().cmp(&left.end.get()))
8009 })
8010}
8011
8012fn numeric_f64(value: Value) -> Option<f64> {
8013 match value.decode()? {
8014 Decoded::Number(number) => Some(number),
8015 Decoded::Int32(raw) => Some(f64::from(raw as i32)),
8016 _ => None,
8017 }
8018}
8019
8020fn number_value(number: f64) -> Value {
8021 if number.is_finite()
8022 && number.fract() == 0.0
8023 && number >= f64::from(i32::MIN)
8024 && number <= f64::from(i32::MAX)
8025 {
8026 Value::int32(number as i32 as u32)
8027 } else {
8028 Value::number(number)
8029 }
8030}
8031
8032fn parse_number(text: &EcmaString) -> f64 {
8033 let Ok(text) = text.to_utf8_strict() else {
8034 return f64::NAN;
8035 };
8036 parse_number_utf8(&text)
8037}
8038
8039fn parse_number_utf8(text: &str) -> f64 {
8040 let trimmed = text.trim();
8041 if trimmed.is_empty() {
8042 0.0
8043 } else {
8044 trimmed.parse::<f64>().unwrap_or(f64::NAN)
8045 }
8046}
8047
8048fn format_number(number: f64) -> String {
8049 if number.is_nan() {
8050 return "NaN".to_owned();
8051 }
8052 if number == f64::INFINITY {
8053 return "Infinity".to_owned();
8054 }
8055 if number == f64::NEG_INFINITY {
8056 return "-Infinity".to_owned();
8057 }
8058 if number == 0.0 {
8059 return "0".to_owned();
8060 }
8061
8062 let negative = number.is_sign_negative();
8063 let raw = number.abs().to_string();
8064 let (mantissa, explicit_exponent) = match raw.split_once(['e', 'E']) {
8065 Some((mantissa, exponent)) => (
8066 mantissa,
8067 exponent
8068 .parse::<i32>()
8069 .expect("Rust formats finite f64 exponents as i32"),
8070 ),
8071 None => (raw.as_str(), 0),
8072 };
8073 let decimal = mantissa.find('.').unwrap_or(mantissa.len());
8074 let untrimmed: String = mantissa.chars().filter(|ch| *ch != '.').collect();
8075 let first = untrimmed
8076 .find(|ch| ch != '0')
8077 .expect("a nonzero number has a nonzero decimal digit");
8078 let digits = untrimmed[first..].trim_end_matches('0');
8079 let exponent = explicit_exponent + decimal as i32 - first as i32 - 1;
8080
8081 let mut result = String::new();
8082 if negative {
8083 result.push('-');
8084 }
8085 if !(-6..21).contains(&exponent) {
8086 result.push(digits.as_bytes()[0] as char);
8087 if digits.len() > 1 {
8088 result.push('.');
8089 result.push_str(&digits[1..]);
8090 }
8091 result.push('e');
8092 if exponent >= 0 {
8093 result.push('+');
8094 }
8095 result.push_str(&exponent.to_string());
8096 } else if exponent >= 0 {
8097 let integer_digits = exponent as usize + 1;
8098 if digits.len() <= integer_digits {
8099 result.push_str(digits);
8100 result.extend(std::iter::repeat_n('0', integer_digits - digits.len()));
8101 } else {
8102 result.push_str(&digits[..integer_digits]);
8103 result.push('.');
8104 result.push_str(&digits[integer_digits..]);
8105 }
8106 } else {
8107 result.push_str("0.");
8108 result.extend(std::iter::repeat_n('0', (-exponent - 1) as usize));
8109 result.push_str(digits);
8110 }
8111 result
8112}
8113
8114fn to_uint32(number: f64) -> u32 {
8115 if !number.is_finite() || number == 0.0 {
8116 0
8117 } else {
8118 number.trunc().rem_euclid(4_294_967_296.0) as u32
8119 }
8120}
8121
8122fn to_int32(number: f64) -> i32 {
8123 to_uint32(number) as i32
8124}
8125
8126fn array_index_ascii(key: &str) -> Option<u32> {
8127 if !key.is_ascii() || key.is_empty() || (key.len() > 1 && key.as_bytes()[0] == b'0') {
8128 return None;
8129 }
8130 let mut index = 0_u32;
8131 for byte in key.bytes() {
8132 if !byte.is_ascii_digit() {
8133 return None;
8134 }
8135 index = index.checked_mul(10)?.checked_add(u32::from(byte - b'0'))?;
8136 }
8137 (index != u32::MAX).then_some(index)
8138}
8139
8140fn array_index(key: &EcmaString) -> Option<u32> {
8141 let units = key.as_units();
8142 if units.is_empty() || (units.len() > 1 && units[0] == u16::from(b'0')) {
8143 return None;
8144 }
8145 let mut index = 0_u32;
8146 for &unit in units {
8147 if !(u16::from(b'0')..=u16::from(b'9')).contains(&unit) {
8148 return None;
8149 }
8150 index = index
8151 .checked_mul(10)?
8152 .checked_add(u32::from(unit - u16::from(b'0')))?;
8153 }
8154 (index != u32::MAX).then_some(index)
8155}
8156
8157fn exact_array_length(value: Value) -> Option<usize> {
8158 let number = numeric_f64(value)?;
8159 if number.is_finite() && number >= 0.0 && number.fract() == 0.0 && number <= u32::MAX as f64 {
8160 Some(number as usize)
8161 } else {
8162 None
8163 }
8164}
8165
8166pub(crate) fn apply_array_length(
8167 elements: &mut Vec<Value>,
8168 properties: &mut PropertyMap,
8169 length: usize,
8170 operation: &'static str,
8171) -> Result<(), EvalFailure> {
8172 if length >= elements.len() {
8173 elements.resize(length, Value::HOLE);
8174 return Ok(());
8175 }
8176 let blocked = properties
8177 .iter()
8178 .filter_map(|(key, property)| {
8179 (!property.configurable())
8180 .then(|| key.as_string().and_then(array_index))
8181 .flatten()
8182 })
8183 .map(|offset| offset as usize)
8184 .filter(|offset| *offset >= length)
8185 .max();
8186 let effective_length = blocked.map_or(length, |offset| offset + 1);
8187 properties.0.retain(|(key, _)| {
8188 key.as_string()
8189 .and_then(array_index)
8190 .is_none_or(|offset| (offset as usize) < effective_length)
8191 });
8192 elements.resize(effective_length, Value::HOLE);
8193 if blocked.is_some() {
8194 return Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }));
8195 }
8196 Ok(())
8197}
8198
8199pub(crate) fn array_set_length(
8200 elements: &mut Vec<Value>,
8201 properties: &mut PropertyMap,
8202 length_writable: bool,
8203 value: Value,
8204 operation: &'static str,
8205) -> Result<(), EvalFailure> {
8206 let length = exact_array_length(value)
8207 .ok_or(EvalFailure::Throw(ThrowOrigin::RangeError { operation }))?;
8208 if !length_writable {
8209 return Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }));
8210 }
8211 apply_array_length(elements, properties, length, operation)
8212}
8213
8214fn bigint_i128(text: &str) -> Result<i128, EvalFailure> {
8215 text.parse::<i128>().map_err(|_| {
8216 EvalFailure::Throw(ThrowOrigin::RangeError {
8217 operation: "bigint magnitude exceeds runtime width",
8218 })
8219 })
8220}
8221
8222fn bigint_binary(op: BinaryOp, left: &str, right: &str) -> Result<String, EvalFailure> {
8223 let left = bigint_i128(left)?;
8224 let right = bigint_i128(right)?;
8225 let overflow =
8226 |operation: &'static str| EvalFailure::Throw(ThrowOrigin::RangeError { operation });
8227 let result = match op {
8228 BinaryOp::Subtract => left
8229 .checked_sub(right)
8230 .ok_or_else(|| overflow("bigint subtract overflow"))?,
8231 BinaryOp::Multiply => left
8232 .checked_mul(right)
8233 .ok_or_else(|| overflow("bigint multiply overflow"))?,
8234 BinaryOp::Divide => {
8235 if right == 0 {
8236 return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8237 operation: "bigint division by zero",
8238 }));
8239 }
8240 left.checked_div(right)
8241 .ok_or_else(|| overflow("bigint divide overflow"))?
8242 }
8243 BinaryOp::Remainder => {
8244 if right == 0 {
8245 return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8246 operation: "bigint remainder by zero",
8247 }));
8248 }
8249 left.checked_rem(right)
8250 .ok_or_else(|| overflow("bigint remainder overflow"))?
8251 }
8252 BinaryOp::Exponent => {
8253 if right < 0 {
8254 return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8255 operation: "bigint negative exponent",
8256 }));
8257 }
8258 let exponent =
8259 u32::try_from(right).map_err(|_| overflow("bigint exponent overflow"))?;
8260 left.checked_pow(exponent)
8261 .ok_or_else(|| overflow("bigint exponent overflow"))?
8262 }
8263 BinaryOp::BitAnd => left & right,
8264 BinaryOp::BitOr => left | right,
8265 BinaryOp::BitXor => left ^ right,
8266 BinaryOp::ShiftLeft | BinaryOp::ShiftRight => {
8267 let left_shift = (op == BinaryOp::ShiftLeft) == (right >= 0);
8268 let amount =
8269 u32::try_from(right.unsigned_abs()).map_err(|_| overflow("bigint shift width"))?;
8270 let shifted = if left_shift {
8271 left.checked_shl(amount)
8272 } else {
8273 left.checked_shr(amount)
8274 };
8275 shifted.ok_or_else(|| overflow("bigint shift overflow"))?
8276 }
8277 BinaryOp::UnsignedShiftRight => {
8278 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
8279 operation: "unsigned shift on bigint",
8280 }));
8281 }
8282 _ => unreachable!("bigint arithmetic partition"),
8283 };
8284 Ok(result.to_string())
8285}
8286
8287pub(crate) fn unary_from_selector(op: u32) -> Option<UnaryOp> {
8288 match op {
8289 0 => Some(UnaryOp::Void),
8290 1 => Some(UnaryOp::TypeOf),
8291 2 => Some(UnaryOp::Plus),
8292 3 => Some(UnaryOp::Negate),
8293 4 => Some(UnaryOp::BitwiseNot),
8294 5 => Some(UnaryOp::LogicalNot),
8295 _ => None,
8296 }
8297}
8298
8299pub(crate) fn binary_from_selector(op: u32) -> Option<BinaryOp> {
8300 match op {
8301 0 => Some(BinaryOp::Add),
8302 1 => Some(BinaryOp::Subtract),
8303 2 => Some(BinaryOp::Multiply),
8304 3 => Some(BinaryOp::Divide),
8305 4 => Some(BinaryOp::Remainder),
8306 5 => Some(BinaryOp::Exponent),
8307 6 => Some(BinaryOp::BitAnd),
8308 7 => Some(BinaryOp::BitOr),
8309 8 => Some(BinaryOp::BitXor),
8310 9 => Some(BinaryOp::ShiftLeft),
8311 10 => Some(BinaryOp::ShiftRight),
8312 11 => Some(BinaryOp::UnsignedShiftRight),
8313 12 => Some(BinaryOp::Equal),
8314 13 => Some(BinaryOp::NotEqual),
8315 14 => Some(BinaryOp::StrictEqual),
8316 15 => Some(BinaryOp::StrictNotEqual),
8317 16 => Some(BinaryOp::LessThan),
8318 17 => Some(BinaryOp::LessThanOrEqual),
8319 18 => Some(BinaryOp::GreaterThan),
8320 19 => Some(BinaryOp::GreaterThanOrEqual),
8321 20 => Some(BinaryOp::InstanceOf),
8322 21 => Some(BinaryOp::In),
8323 _ => None,
8324 }
8325}
8326
8327pub(crate) fn iterator_kind_from_selector(kind: u32) -> Option<IteratorKind> {
8328 match kind {
8329 0 => Some(IteratorKind::Sync),
8330 1 => Some(IteratorKind::Async),
8331 2 => Some(IteratorKind::Keys),
8332 _ => None,
8333 }
8334}
8335
8336pub(crate) fn accessor_from_selector(kind: u32) -> Option<AccessorKind> {
8337 match kind {
8338 0 => Some(AccessorKind::Getter),
8339 1 => Some(AccessorKind::Setter),
8340 _ => None,
8341 }
8342}
8343
8344#[cfg(test)]
8345mod tests {
8346 use std::sync::Arc;
8347
8348 use super::*;
8349 use crate::intrinsics::BuiltinOutcome;
8350 use bamts_bytecode::{
8351 Binding, Edge, EdgeKind, ExceptionHandler, Export, ExportSource, FunctionFlags, NumberBits,
8352 ProgramModule, Register,
8353 };
8354
8355 fn reg(raw: u32) -> Register {
8356 Register::new(raw)
8357 }
8358 fn pc(raw: u32) -> Pc {
8359 Pc::new(raw)
8360 }
8361 fn cid(raw: u32) -> ConstantId {
8362 ConstantId::new(raw)
8363 }
8364
8365 fn function(
8367 parameters: u32,
8368 registers: u32,
8369 code: Vec<Instruction>,
8370 handlers: Vec<ExceptionHandler>,
8371 ) -> Function {
8372 Function::new(
8373 None,
8374 0,
8375 parameters,
8376 registers,
8377 FunctionFlags::default(),
8378 code,
8379 handlers,
8380 )
8381 }
8382
8383 fn generator_function(
8384 parameters: u32,
8385 registers: u32,
8386 code: Vec<Instruction>,
8387 handlers: Vec<ExceptionHandler>,
8388 ) -> Function {
8389 Function::new(
8390 None,
8391 0,
8392 parameters,
8393 registers,
8394 FunctionFlags {
8395 is_async: false,
8396 is_generator: true,
8397 },
8398 code,
8399 handlers,
8400 )
8401 }
8402
8403 fn async_function(
8404 parameters: u32,
8405 registers: u32,
8406 code: Vec<Instruction>,
8407 handlers: Vec<ExceptionHandler>,
8408 ) -> Function {
8409 Function::new(
8410 None,
8411 0,
8412 parameters,
8413 registers,
8414 FunctionFlags {
8415 is_async: true,
8416 is_generator: false,
8417 },
8418 code,
8419 handlers,
8420 )
8421 }
8422
8423 fn closure_function(
8425 captures: u32,
8426 parameters: u32,
8427 registers: u32,
8428 code: Vec<Instruction>,
8429 ) -> Function {
8430 Function::new(
8431 None,
8432 captures,
8433 parameters,
8434 registers,
8435 FunctionFlags::default(),
8436 code,
8437 Vec::new(),
8438 )
8439 }
8440
8441 fn verified(mut constants: Vec<Constant>, functions: Vec<Function>) -> Program<Verified> {
8442 let name = ConstantId::new(constants.len() as u32);
8443 constants.push(Constant::String(EcmaString::from_utf8("<test>")));
8444 let code = Module::new(constants, functions, FunctionId::new(0))
8445 .verify()
8446 .expect("valid test bytecode");
8447 Program::link(
8448 vec![ProgramModule {
8449 name,
8450 code,
8451 edges: Vec::new(),
8452 bindings: Vec::new(),
8453 exports: Vec::new(),
8454 }],
8455 ModuleId::new(0),
8456 )
8457 .expect("valid one-module test program")
8458 }
8459 fn program_module(
8460 name: &str,
8461 mut constants: Vec<Constant>,
8462 functions: Vec<Function>,
8463 edges: Vec<Edge>,
8464 bindings: Vec<Binding>,
8465 exports: Vec<Export>,
8466 ) -> ProgramModule<Verified> {
8467 constants.insert(0, Constant::String(EcmaString::from_utf8(name)));
8468 let code = Module::new(constants, functions, FunctionId::new(0))
8469 .verify()
8470 .expect("valid test bytecode");
8471 ProgramModule {
8472 name: ConstantId::new(0),
8473 code,
8474 edges,
8475 bindings,
8476 exports,
8477 }
8478 }
8479
8480 fn linked(modules: Vec<ProgramModule<Verified>>, entry: u32) -> Program<Verified> {
8481 Program::link(modules, ModuleId::new(entry)).expect("valid linked test program")
8482 }
8483
8484 fn namespace_descriptor_entry() -> Function {
8485 function(
8486 0,
8487 7,
8488 vec![
8489 Instruction::LoadGlobal {
8490 dst: reg(0),
8491 name: cid(1),
8492 },
8493 Instruction::LoadGlobal {
8494 dst: reg(1),
8495 name: cid(3),
8496 },
8497 Instruction::LoadConst {
8498 dst: reg(2),
8499 constant: cid(4),
8500 },
8501 Instruction::GetProperty {
8502 dst: reg(3),
8503 object: reg(1),
8504 key: reg(2),
8505 },
8506 Instruction::CreateArray { dst: reg(4) },
8507 Instruction::ArrayPush {
8508 array: reg(4),
8509 value: reg(0),
8510 },
8511 Instruction::LoadConst {
8512 dst: reg(5),
8513 constant: cid(5),
8514 },
8515 Instruction::ArrayPush {
8516 array: reg(4),
8517 value: reg(5),
8518 },
8519 Instruction::Call {
8520 dst: reg(6),
8521 callee: reg(3),
8522 this_value: reg(4),
8523 arguments: reg(4),
8524 },
8525 Instruction::Return { value: reg(6) },
8526 ],
8527 Vec::new(),
8528 )
8529 }
8530
8531 #[derive(Default)]
8532 struct TestHost;
8533 impl Host for TestHost {}
8534
8535 #[test]
8536 fn async_await_setup_failure_releases_suspended_registers() {
8537 let program = verified(
8538 vec![Constant::Undefined],
8539 vec![
8540 function(0, 1, vec![Instruction::Halt], Vec::new()),
8541 async_function(
8542 0,
8543 2,
8544 vec![
8545 Instruction::LoadConst {
8546 dst: reg(0),
8547 constant: cid(0),
8548 },
8549 Instruction::Suspend {
8550 dst: reg(1),
8551 src: reg(0),
8552 resume: pc(2),
8553 },
8554 Instruction::Return { value: reg(1) },
8555 ],
8556 Vec::new(),
8557 ),
8558 ],
8559 );
8560 let mut host = TestHost;
8561 let limits = Limits {
8562 max_microtasks: 0,
8563 ..Limits::default()
8564 };
8565 let mut machine = Machine::new(&program, &mut host, limits);
8566 machine.frames.clear();
8567 machine.live_registers = 0;
8568 let callable = generator_callable(&mut machine, 1);
8569
8570 assert!(matches!(
8571 machine.call_value(callable, Value::UNDEFINED, &[]),
8572 Err(EvalFailure::Runtime(
8573 RuntimeErrorKind::MicrotaskQueueLimitExceeded { limit: 0 }
8574 ))
8575 ));
8576 assert_eq!(machine.live_registers, 0);
8577 }
8578
8579 fn run_ok(program: &Program<Verified>) -> Execution {
8580 let mut host = TestHost;
8581 Machine::new(program, &mut host, Limits::default())
8582 .run()
8583 .unwrap()
8584 }
8585
8586 fn generator_callable<H: Host>(machine: &mut Machine<'_, H>, function: u32) -> Value {
8587 machine
8588 .allocate(HeapEntry::Function {
8589 module: ModuleId::new(0),
8590 function: FunctionId::new(function),
8591 captures: Vec::new(),
8592 properties: PropertyMap::default(),
8593 prototype: Some(machine.intrinsics.function_prototype),
8594 extensible: true,
8595 })
8596 .unwrap()
8597 }
8598
8599 fn generator_next<H: Host>(
8600 machine: &mut Machine<'_, H>,
8601 generator: Value,
8602 resume_value: Value,
8603 ) -> Result<(Value, bool), EvalFailure> {
8604 let next = machine.get_named_property(generator, "next")?;
8605 let result = machine.call_value(next, generator, &[resume_value])?;
8606 let done = machine.get_named_property(result, "done")?;
8607 let value = machine.get_named_property(result, "value")?;
8608 Ok((value, machine.truthy(done)))
8609 }
8610
8611 #[test]
8612 fn sync_generator_is_lazy_resumes_registers_and_stays_completed() {
8613 let program = verified(
8614 vec![Constant::Int32(10)],
8615 vec![
8616 function(0, 1, vec![Instruction::Halt], Vec::new()),
8617 generator_function(
8618 0,
8619 3,
8620 vec![
8621 Instruction::LoadConst {
8622 dst: reg(0),
8623 constant: cid(0),
8624 },
8625 Instruction::Suspend {
8626 dst: reg(1),
8627 src: reg(0),
8628 resume: pc(2),
8629 },
8630 Instruction::Binary {
8631 dst: reg(2),
8632 op: BinaryOp::Add,
8633 left: reg(0),
8634 right: reg(1),
8635 },
8636 Instruction::Return { value: reg(2) },
8637 ],
8638 Vec::new(),
8639 ),
8640 ],
8641 );
8642 let mut host = TestHost;
8643 let mut machine = Machine::new(&program, &mut host, Limits::default());
8644 machine.frames.clear();
8645 machine.live_registers = 0;
8646 let callable = generator_callable(&mut machine, 1);
8647 let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8648 assert_eq!(machine.live_registers, 0, "calling must not start the body");
8649 machine
8650 .set_data_property(generator, "visible", Value::int32(1))
8651 .unwrap();
8652 assert_eq!(
8653 machine.get_named_property(generator, "visible").unwrap(),
8654 Value::int32(1),
8655 );
8656 assert_eq!(
8657 machine.own_property_keys(generator).unwrap(),
8658 vec![PropertyKey::Named(EcmaString::from_utf8("visible"))],
8659 );
8660 assert!(
8661 machine
8662 .inherits_from_prototype(
8663 generator,
8664 machine.intrinsics.builtins.generator_prototype(),
8665 )
8666 .unwrap()
8667 );
8668
8669 assert_eq!(
8670 generator_next(&mut machine, generator, Value::int32(99)).unwrap(),
8671 (Value::int32(10), false),
8672 );
8673 assert_eq!(machine.live_registers, 3);
8674 assert_eq!(
8675 generator_next(&mut machine, generator, Value::int32(5)).unwrap(),
8676 (Value::int32(15), true),
8677 );
8678 assert_eq!(machine.live_registers, 0);
8679 assert_eq!(
8680 generator_next(&mut machine, generator, Value::int32(8)).unwrap(),
8681 (Value::UNDEFINED, true),
8682 );
8683 }
8684
8685 #[test]
8686 fn sync_generator_reentrant_next_is_a_type_error() {
8687 let program = verified(
8688 Vec::new(),
8689 vec![
8690 function(0, 1, vec![Instruction::Halt], Vec::new()),
8691 generator_function(0, 1, vec![Instruction::Halt], Vec::new()),
8692 ],
8693 );
8694 let mut host = TestHost;
8695 let mut machine = Machine::new(&program, &mut host, Limits::default());
8696 machine.frames.clear();
8697 machine.live_registers = 0;
8698 let callable = generator_callable(&mut machine, 1);
8699 let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8700 let _ = machine.take_generator_state(generator).unwrap();
8701
8702 assert!(matches!(
8703 generator_next(&mut machine, generator, Value::UNDEFINED),
8704 Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
8705 ));
8706 }
8707
8708 #[test]
8709 fn sync_generator_uncaught_throw_preserves_origin_and_completes() {
8710 let program = verified(
8711 vec![Constant::Int32(7)],
8712 vec![
8713 function(0, 1, vec![Instruction::Halt], Vec::new()),
8714 generator_function(
8715 0,
8716 1,
8717 vec![
8718 Instruction::LoadConst {
8719 dst: reg(0),
8720 constant: cid(0),
8721 },
8722 Instruction::Throw { value: reg(0) },
8723 ],
8724 Vec::new(),
8725 ),
8726 ],
8727 );
8728 let mut host = TestHost;
8729 let mut machine = Machine::new(&program, &mut host, Limits::default());
8730 machine.frames.clear();
8731 machine.live_registers = 0;
8732 let callable = generator_callable(&mut machine, 1);
8733 let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8734
8735 assert!(matches!(
8736 generator_next(&mut machine, generator, Value::UNDEFINED),
8737 Err(EvalFailure::ThrowValueOrigin {
8738 value,
8739 origin: ThrowOrigin::Bytecode,
8740 }) if value == Value::int32(7)
8741 ));
8742 assert_eq!(
8743 generator_next(&mut machine, generator, Value::UNDEFINED).unwrap(),
8744 (Value::UNDEFINED, true),
8745 );
8746 assert_eq!(machine.live_registers, 0);
8747 }
8748
8749 #[test]
8750 fn outer_compiled_handler_catches_generator_throw_value() {
8751 let program = verified(
8752 vec![
8753 Constant::Int32(7),
8754 Constant::Undefined,
8755 Constant::String(EcmaString::from_utf8("next")),
8756 ],
8757 vec![
8758 function(
8759 0,
8760 8,
8761 vec![
8762 Instruction::CreateArray { dst: reg(0) },
8763 Instruction::CreateClosure {
8764 dst: reg(1),
8765 function: FunctionId::new(1),
8766 captures: reg(0),
8767 },
8768 Instruction::CreateArray { dst: reg(2) },
8769 Instruction::LoadConst {
8770 dst: reg(3),
8771 constant: cid(1),
8772 },
8773 Instruction::Call {
8774 dst: reg(4),
8775 callee: reg(1),
8776 this_value: reg(3),
8777 arguments: reg(2),
8778 },
8779 Instruction::LoadConst {
8780 dst: reg(5),
8781 constant: cid(2),
8782 },
8783 Instruction::GetProperty {
8784 dst: reg(6),
8785 object: reg(4),
8786 key: reg(5),
8787 },
8788 Instruction::Call {
8789 dst: reg(7),
8790 callee: reg(6),
8791 this_value: reg(4),
8792 arguments: reg(2),
8793 },
8794 Instruction::Return { value: reg(3) },
8795 Instruction::Return { value: reg(7) },
8796 ],
8797 vec![ExceptionHandler {
8798 start: pc(7),
8799 end: pc(8),
8800 handler: pc(9),
8801 catch_register: reg(7),
8802 }],
8803 ),
8804 generator_function(
8805 0,
8806 1,
8807 vec![
8808 Instruction::LoadConst {
8809 dst: reg(0),
8810 constant: cid(0),
8811 },
8812 Instruction::Throw { value: reg(0) },
8813 ],
8814 Vec::new(),
8815 ),
8816 ],
8817 );
8818
8819 assert_eq!(run_ok(&program).value, Value::int32(7));
8820 }
8821
8822 #[test]
8823 fn sync_generator_catches_body_throw_before_suspending() {
8824 let program = verified(
8825 vec![Constant::Int32(7)],
8826 vec![
8827 function(0, 1, vec![Instruction::Halt], Vec::new()),
8828 generator_function(
8829 0,
8830 3,
8831 vec![
8832 Instruction::LoadConst {
8833 dst: reg(0),
8834 constant: cid(0),
8835 },
8836 Instruction::Throw { value: reg(0) },
8837 Instruction::Suspend {
8838 dst: reg(2),
8839 src: reg(1),
8840 resume: pc(3),
8841 },
8842 Instruction::Return { value: reg(2) },
8843 ],
8844 vec![ExceptionHandler {
8845 start: pc(1),
8846 end: pc(2),
8847 handler: pc(2),
8848 catch_register: reg(1),
8849 }],
8850 ),
8851 ],
8852 );
8853 let mut host = TestHost;
8854 let mut machine = Machine::new(&program, &mut host, Limits::default());
8855 machine.frames.clear();
8856 machine.live_registers = 0;
8857 let callable = generator_callable(&mut machine, 1);
8858 let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8859
8860 assert_eq!(
8861 generator_next(&mut machine, generator, Value::UNDEFINED).unwrap(),
8862 (Value::int32(7), false),
8863 );
8864 assert_eq!(
8865 generator_next(&mut machine, generator, Value::int32(9)).unwrap(),
8866 (Value::int32(9), true),
8867 );
8868 }
8869
8870 #[test]
8871 fn suspended_generator_registers_remain_charged() {
8872 let program = verified(
8873 vec![Constant::Int32(1)],
8874 vec![
8875 function(0, 1, vec![Instruction::Halt], Vec::new()),
8876 generator_function(
8877 0,
8878 3,
8879 vec![
8880 Instruction::LoadConst {
8881 dst: reg(0),
8882 constant: cid(0),
8883 },
8884 Instruction::Suspend {
8885 dst: reg(1),
8886 src: reg(0),
8887 resume: pc(2),
8888 },
8889 Instruction::Return { value: reg(1) },
8890 ],
8891 Vec::new(),
8892 ),
8893 ],
8894 );
8895 let mut host = TestHost;
8896 let mut machine = Machine::new(
8897 &program,
8898 &mut host,
8899 Limits {
8900 max_total_registers: 3,
8901 ..Limits::default()
8902 },
8903 );
8904 machine.frames.clear();
8905 machine.live_registers = 0;
8906 let callable = generator_callable(&mut machine, 1);
8907 let first = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8908 let second = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8909 assert_eq!(
8910 generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
8911 (Value::int32(1), false),
8912 );
8913 assert!(matches!(
8914 generator_next(&mut machine, second, Value::UNDEFINED),
8915 Err(EvalFailure::Runtime(
8916 RuntimeErrorKind::RegisterLimitExceeded { .. }
8917 ))
8918 ));
8919 assert_eq!(machine.live_registers, 3);
8920 assert_eq!(
8921 generator_next(&mut machine, first, Value::int32(4)).unwrap(),
8922 (Value::int32(4), true),
8923 );
8924 assert_eq!(machine.live_registers, 0);
8925 }
8926
8927 #[test]
8928 fn resumed_generator_call_depth_failure_releases_registers() {
8929 let program = verified(
8930 vec![Constant::Int32(1)],
8931 vec![
8932 function(0, 1, vec![Instruction::Halt], Vec::new()),
8933 generator_function(
8934 0,
8935 2,
8936 vec![
8937 Instruction::LoadConst {
8938 dst: reg(0),
8939 constant: cid(0),
8940 },
8941 Instruction::Suspend {
8942 dst: reg(1),
8943 src: reg(0),
8944 resume: pc(2),
8945 },
8946 Instruction::Return { value: reg(1) },
8947 ],
8948 Vec::new(),
8949 ),
8950 ],
8951 );
8952 let mut host = TestHost;
8953 let mut machine = Machine::new(
8954 &program,
8955 &mut host,
8956 Limits {
8957 max_total_registers: 2,
8958 ..Limits::default()
8959 },
8960 );
8961 machine.frames.clear();
8962 machine.live_registers = 0;
8963
8964 let callable = generator_callable(&mut machine, 1);
8965 let first = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8966
8967 assert_eq!(
8969 generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
8970 (Value::int32(1), false),
8971 );
8972 assert_eq!(machine.live_registers, 2);
8973
8974 machine.frames.push(Frame {
8977 module: ModuleId::new(0),
8978 function: 0,
8979 pc: 0,
8980 registers: Vec::new(),
8981 return_to: None,
8982 this_value: Value::UNDEFINED,
8983 new_target: Value::UNDEFINED,
8984 args: Vec::new(),
8985 arguments_object: None,
8986 });
8987 machine.limits.max_call_depth = machine.frames.len();
8988
8989 assert!(matches!(
8990 generator_next(&mut machine, first, Value::int32(7)),
8991 Err(EvalFailure::Runtime(
8992 RuntimeErrorKind::CallDepthExceeded { .. }
8993 ))
8994 ));
8995 assert_eq!(machine.live_registers, 0);
8996
8997 assert_eq!(
8999 generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
9000 (Value::UNDEFINED, true),
9001 );
9002
9003 machine.frames.pop();
9005 machine.limits.max_call_depth = Limits::default().max_call_depth;
9006
9007 let second = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
9009 assert_eq!(
9010 generator_next(&mut machine, second, Value::UNDEFINED).unwrap(),
9011 (Value::int32(1), false),
9012 );
9013 assert_eq!(machine.live_registers, 2);
9014 assert_eq!(
9015 generator_next(&mut machine, second, Value::int32(9)).unwrap(),
9016 (Value::int32(9), true),
9017 );
9018 assert_eq!(machine.live_registers, 0);
9019 }
9020 #[test]
9021 fn array_extend_consumes_generator_through_sync_iterator_protocol() {
9022 let program = verified(
9023 vec![Constant::Int32(1), Constant::Int32(2)],
9024 vec![
9025 function(0, 1, vec![Instruction::Halt], Vec::new()),
9026 generator_function(
9027 0,
9028 3,
9029 vec![
9030 Instruction::LoadConst {
9031 dst: reg(0),
9032 constant: cid(0),
9033 },
9034 Instruction::Suspend {
9035 dst: reg(2),
9036 src: reg(0),
9037 resume: pc(2),
9038 },
9039 Instruction::LoadConst {
9040 dst: reg(1),
9041 constant: cid(1),
9042 },
9043 Instruction::Suspend {
9044 dst: reg(2),
9045 src: reg(1),
9046 resume: pc(4),
9047 },
9048 Instruction::Return { value: reg(2) },
9049 ],
9050 Vec::new(),
9051 ),
9052 ],
9053 );
9054 let mut host = TestHost;
9055 let mut machine = Machine::new(&program, &mut host, Limits::default());
9056 machine.frames.clear();
9057 machine.live_registers = 0;
9058 let callable = generator_callable(&mut machine, 1);
9059 let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
9060 let array = machine
9061 .allocate(HeapEntry::Array {
9062 elements: Vec::new(),
9063 properties: PropertyMap::default(),
9064 prototype: Some(machine.intrinsics.array_prototype),
9065 extensible: true,
9066 length_writable: true,
9067 })
9068 .unwrap();
9069
9070 machine.array_extend(array, generator).unwrap();
9071 assert_eq!(
9072 machine.array_elements(array).unwrap(),
9073 Some(vec![Value::int32(1), Value::int32(2)]),
9074 );
9075 assert_eq!(machine.live_registers, 0);
9076 }
9077
9078 #[test]
9079 fn runtime_callback_without_interpreter_caller_propagates_throw() {
9080 let program = verified(
9081 Vec::new(),
9082 vec![
9083 function(0, 1, vec![Instruction::Halt], Vec::new()),
9084 function(1, 1, vec![Instruction::Throw { value: reg(0) }], Vec::new()),
9085 ],
9086 );
9087 let mut host = TestHost;
9088 let mut machine = Machine::new(&program, &mut host, Limits::default());
9089 machine.frames.clear();
9090 machine.live_registers = 0;
9091 let callee = machine
9092 .allocate(HeapEntry::Function {
9093 module: ModuleId::new(0),
9094 function: FunctionId::new(1),
9095 captures: Vec::new(),
9096 properties: PropertyMap::default(),
9097 prototype: Some(machine.intrinsics.function_prototype),
9098 extensible: true,
9099 })
9100 .unwrap();
9101 let thrown = Value::int32(7);
9102
9103 assert!(matches!(
9104 machine.call_value(callee, Value::UNDEFINED, &[thrown]),
9105 Err(EvalFailure::ThrowValue(value)) if value == thrown
9106 ));
9107 }
9108
9109 #[test]
9110 fn runtime_callback_failure_releases_root_frame() {
9111 let program = verified(
9112 Vec::new(),
9113 vec![
9114 function(0, 1, vec![Instruction::Halt], Vec::new()),
9115 function(
9116 1,
9117 1,
9118 vec![Instruction::Return { value: reg(0) }],
9119 Vec::new(),
9120 ),
9121 ],
9122 );
9123 let mut host = TestHost;
9124 let mut machine = Machine::new(&program, &mut host, Limits::default());
9125 machine.frames.clear();
9126 machine.live_registers = 0;
9127 let callee = machine
9128 .allocate(HeapEntry::Function {
9129 module: ModuleId::new(0),
9130 function: FunctionId::new(1),
9131 captures: Vec::new(),
9132 properties: PropertyMap::default(),
9133 prototype: Some(machine.intrinsics.function_prototype),
9134 extensible: true,
9135 })
9136 .unwrap();
9137 machine.fuel = 0;
9138
9139 assert!(matches!(
9140 machine.call_value(callee, Value::UNDEFINED, &[Value::int32(7)]),
9141 Err(EvalFailure::Runtime(RuntimeErrorKind::FuelExhausted { .. }))
9142 ));
9143 assert!(machine.frames.is_empty());
9144 assert_eq!(machine.live_registers, 0);
9145
9146 machine.fuel = 1;
9147 assert!(matches!(
9148 machine.call_value(callee, Value::UNDEFINED, &[Value::int32(7)]),
9149 Ok(value) if value == Value::int32(7)
9150 ));
9151 }
9152
9153 #[test]
9154 fn object_values_have_stable_distinct_heap_identity() {
9155 let module = verified(
9156 vec![],
9157 vec![function(
9158 0,
9159 5,
9160 vec![
9161 Instruction::CreateObject { dst: reg(0) },
9162 Instruction::CreateObject { dst: reg(1) },
9163 Instruction::Binary {
9164 dst: reg(2),
9165 op: BinaryOp::StrictEqual,
9166 left: reg(0),
9167 right: reg(1),
9168 },
9169 Instruction::Move {
9170 dst: reg(3),
9171 src: reg(0),
9172 },
9173 Instruction::Binary {
9174 dst: reg(4),
9175 op: BinaryOp::StrictEqual,
9176 left: reg(0),
9177 right: reg(3),
9178 },
9179 Instruction::Return { value: reg(4) },
9180 ],
9181 vec![],
9182 )],
9183 );
9184 let execution = run_ok(&module);
9185 assert_eq!(execution.entry_registers[2], Value::FALSE);
9186 assert_eq!(execution.value, Value::TRUE);
9187 }
9188
9189 #[test]
9190 fn addition_coerces_objects_left_to_right_and_interpolates_errors() {
9191 let module = verified(
9192 vec![
9193 Constant::String(EcmaString::from_utf8("L")),
9194 Constant::String(EcmaString::from_utf8("additionOrder")),
9195 Constant::String(EcmaString::from_utf8("message")),
9196 ],
9197 vec![
9198 function(0, 1, vec![Instruction::Halt], Vec::new()),
9199 function(
9200 0,
9201 1,
9202 vec![
9203 Instruction::LoadConst {
9204 dst: reg(0),
9205 constant: cid(0),
9206 },
9207 Instruction::StoreGlobal {
9208 name: cid(1),
9209 value: reg(0),
9210 },
9211 Instruction::Return { value: reg(0) },
9212 ],
9213 Vec::new(),
9214 ),
9215 function(
9216 0,
9217 1,
9218 vec![
9219 Instruction::LoadGlobal {
9220 dst: reg(0),
9221 name: cid(1),
9222 },
9223 Instruction::Return { value: reg(0) },
9224 ],
9225 Vec::new(),
9226 ),
9227 ],
9228 );
9229 let mut host = TestHost;
9230 let mut machine = Machine::new(&module, &mut host, Limits::default());
9231 machine.frames.clear();
9232 machine.live_registers = 0;
9233 let left = machine
9234 .allocate(HeapEntry::Object {
9235 properties: PropertyMap::default(),
9236 prototype: Some(machine.intrinsics.object_prototype),
9237 extensible: true,
9238 boxed_primitive: None,
9239 })
9240 .unwrap();
9241 let right = machine
9242 .allocate(HeapEntry::Object {
9243 properties: PropertyMap::default(),
9244 prototype: Some(machine.intrinsics.object_prototype),
9245 extensible: true,
9246 boxed_primitive: None,
9247 })
9248 .unwrap();
9249 let left_value_of = machine
9250 .allocate(HeapEntry::Function {
9251 module: ModuleId::new(0),
9252 function: FunctionId::new(1),
9253 captures: Vec::new(),
9254 properties: PropertyMap::default(),
9255 prototype: Some(machine.intrinsics.function_prototype),
9256 extensible: true,
9257 })
9258 .unwrap();
9259 let right_value_of = machine
9260 .allocate(HeapEntry::Function {
9261 module: ModuleId::new(0),
9262 function: FunctionId::new(2),
9263 captures: Vec::new(),
9264 properties: PropertyMap::default(),
9265 prototype: Some(machine.intrinsics.function_prototype),
9266 extensible: true,
9267 })
9268 .unwrap();
9269 machine
9270 .set_data_property(left, "valueOf", left_value_of)
9271 .unwrap();
9272 machine
9273 .set_data_property(right, "valueOf", right_value_of)
9274 .unwrap();
9275 let coerced = machine.add(left, right).unwrap();
9276 assert!(
9277 machine
9278 .string_value(coerced)
9279 .is_some_and(|text| text.eq_ascii("LL"))
9280 );
9281
9282 let error_constructor = machine.intrinsics.global("Error").unwrap();
9283 let message = machine
9284 .allocate(HeapEntry::String(EcmaString::from_utf8("message")))
9285 .unwrap();
9286 let error = machine
9287 .call_value(error_constructor, Value::UNDEFINED, &[message])
9288 .unwrap();
9289 let empty = machine
9290 .allocate(HeapEntry::String(EcmaString::default()))
9291 .unwrap();
9292 let interpolated = machine.add(empty, error).unwrap();
9293 assert!(
9294 machine
9295 .string_value(interpolated)
9296 .is_some_and(|text| text.eq_ascii("Error: message"))
9297 );
9298
9299 let date_constructor = machine.intrinsics.global("Date").unwrap();
9300 let date_prototype = machine
9301 .get_named_property(date_constructor, "prototype")
9302 .unwrap();
9303 let date = machine
9304 .allocate(HeapEntry::Date {
9305 time: 0.0,
9306 properties: PropertyMap::default(),
9307 prototype: Some(date_prototype),
9308 extensible: true,
9309 })
9310 .unwrap();
9311 machine
9312 .set_data_property(date, "toString", left_value_of)
9313 .unwrap();
9314 let date_text = machine.add(date, empty).unwrap();
9315 assert!(
9316 machine
9317 .string_value(date_text)
9318 .is_some_and(|text| text.eq_ascii("L"))
9319 );
9320 }
9321
9322 #[test]
9323 fn computed_member_access_uses_dynamic_register_key() {
9324 let module = verified(
9326 vec![
9327 Constant::String(EcmaString::from_utf8("a")),
9328 Constant::String(EcmaString::from_utf8("b")),
9329 Constant::Int32(7),
9330 ],
9331 vec![function(
9332 0,
9333 6,
9334 vec![
9335 Instruction::LoadConst {
9336 dst: reg(1),
9337 constant: cid(0),
9338 },
9339 Instruction::LoadConst {
9340 dst: reg(2),
9341 constant: cid(1),
9342 },
9343 Instruction::Binary {
9344 dst: reg(3),
9345 op: BinaryOp::Add,
9346 left: reg(1),
9347 right: reg(2),
9348 },
9349 Instruction::CreateObject { dst: reg(0) },
9350 Instruction::LoadConst {
9351 dst: reg(4),
9352 constant: cid(2),
9353 },
9354 Instruction::SetProperty {
9355 object: reg(0),
9356 key: reg(3),
9357 value: reg(4),
9358 },
9359 Instruction::GetProperty {
9360 dst: reg(5),
9361 object: reg(0),
9362 key: reg(3),
9363 },
9364 Instruction::Return { value: reg(5) },
9365 ],
9366 vec![],
9367 )],
9368 );
9369 assert_eq!(run_ok(&module).value, Value::int32(7));
9370 }
9371
9372 #[test]
9373 fn property_delete_and_array_holes_are_real_mutations() {
9374 let module = verified(
9375 vec![
9376 Constant::String(EcmaString::from_utf8("0")),
9377 Constant::Int32(5),
9378 ],
9379 vec![function(
9380 0,
9381 5,
9382 vec![
9383 Instruction::CreateArray { dst: reg(0) },
9384 Instruction::LoadConst {
9385 dst: reg(1),
9386 constant: cid(0),
9387 },
9388 Instruction::LoadConst {
9389 dst: reg(4),
9390 constant: cid(1),
9391 },
9392 Instruction::SetProperty {
9393 object: reg(0),
9394 key: reg(1),
9395 value: reg(4),
9396 },
9397 Instruction::GetProperty {
9398 dst: reg(2),
9399 object: reg(0),
9400 key: reg(1),
9401 },
9402 Instruction::DeleteProperty {
9403 dst: reg(3),
9404 object: reg(0),
9405 key: reg(1),
9406 },
9407 Instruction::GetProperty {
9408 dst: reg(4),
9409 object: reg(0),
9410 key: reg(1),
9411 },
9412 Instruction::Return { value: reg(3) },
9413 ],
9414 vec![],
9415 )],
9416 );
9417 let execution = run_ok(&module);
9418 assert_eq!(execution.entry_registers[2], Value::int32(5));
9419 assert_eq!(execution.entry_registers[4], Value::UNDEFINED);
9420 assert_eq!(execution.value, Value::TRUE);
9421 }
9422
9423 #[test]
9424 fn closure_captures_seed_leading_registers_before_parameters() {
9425 let entry = function(
9427 0,
9428 3,
9429 vec![
9430 Instruction::CreateArray { dst: reg(0) },
9431 Instruction::LoadConst {
9432 dst: reg(1),
9433 constant: cid(0),
9434 },
9435 Instruction::ArrayPush {
9436 array: reg(0),
9437 value: reg(1),
9438 },
9439 Instruction::CreateClosure {
9440 dst: reg(2),
9441 function: FunctionId::new(1),
9442 captures: reg(0),
9443 },
9444 Instruction::CreateArray { dst: reg(0) },
9446 Instruction::LoadConst {
9447 dst: reg(1),
9448 constant: cid(1),
9449 },
9450 Instruction::ArrayPush {
9451 array: reg(0),
9452 value: reg(1),
9453 },
9454 Instruction::LoadConst {
9455 dst: reg(1),
9456 constant: cid(2),
9457 },
9458 Instruction::Call {
9459 dst: reg(1),
9460 callee: reg(2),
9461 this_value: reg(1),
9462 arguments: reg(0),
9463 },
9464 Instruction::Return { value: reg(1) },
9465 ],
9466 vec![],
9467 );
9468 let callee = closure_function(
9470 1,
9471 1,
9472 3,
9473 vec![
9474 Instruction::Binary {
9475 dst: reg(2),
9476 op: BinaryOp::Add,
9477 left: reg(0),
9478 right: reg(1),
9479 },
9480 Instruction::Return { value: reg(2) },
9481 ],
9482 );
9483 let module = verified(
9484 vec![Constant::Int32(42), Constant::Int32(7), Constant::Undefined],
9485 vec![entry, callee],
9486 );
9487 assert_eq!(run_ok(&module).value, Value::int32(49));
9488 }
9489
9490 #[test]
9491 fn calls_scale_past_fixed_window_via_arguments_array() {
9492 let mut code = vec![Instruction::CreateArray { dst: reg(0) }];
9495 code.push(Instruction::LoadConst {
9496 dst: reg(1),
9497 constant: cid(0),
9498 });
9499 for _ in 0..500 {
9500 code.push(Instruction::ArrayPush {
9501 array: reg(0),
9502 value: reg(1),
9503 });
9504 }
9505 code.push(Instruction::CreateClosure {
9506 dst: reg(2),
9507 function: FunctionId::new(1),
9508 captures: reg(3),
9509 });
9510 let mut prelude = vec![Instruction::CreateArray { dst: reg(3) }];
9514 prelude.append(&mut code);
9515 let mut code = prelude;
9516 code.push(Instruction::LoadConst {
9517 dst: reg(1),
9518 constant: cid(1),
9519 });
9520 code.push(Instruction::Call {
9521 dst: reg(1),
9522 callee: reg(2),
9523 this_value: reg(1),
9524 arguments: reg(0),
9525 });
9526 code.push(Instruction::Return { value: reg(1) });
9527
9528 let entry = function(0, 4, code, vec![]);
9529 let callee = function(
9530 0,
9531 2,
9532 vec![
9533 Instruction::LoadArguments { dst: reg(0) },
9534 Instruction::LoadConst {
9535 dst: reg(1),
9536 constant: cid(2),
9537 },
9538 Instruction::GetProperty {
9539 dst: reg(0),
9540 object: reg(0),
9541 key: reg(1),
9542 },
9543 Instruction::Return { value: reg(0) },
9544 ],
9545 vec![],
9546 );
9547 let module = verified(
9548 vec![
9549 Constant::Int32(1),
9550 Constant::Undefined,
9551 Constant::String(EcmaString::from_utf8("length")),
9552 ],
9553 vec![entry, callee],
9554 );
9555 assert_eq!(run_ok(&module).value, Value::int32(500));
9556 }
9557
9558 #[test]
9559 fn array_extend_spreads_iterable_elements() {
9560 let entry = function(
9562 0,
9563 4,
9564 vec![
9565 Instruction::CreateArray { dst: reg(0) },
9566 Instruction::LoadConst {
9567 dst: reg(1),
9568 constant: cid(0),
9569 },
9570 Instruction::ArrayPush {
9571 array: reg(0),
9572 value: reg(1),
9573 },
9574 Instruction::CreateArray { dst: reg(2) },
9576 Instruction::LoadConst {
9577 dst: reg(1),
9578 constant: cid(1),
9579 },
9580 Instruction::ArrayPush {
9581 array: reg(2),
9582 value: reg(1),
9583 },
9584 Instruction::LoadConst {
9585 dst: reg(1),
9586 constant: cid(2),
9587 },
9588 Instruction::ArrayPush {
9589 array: reg(2),
9590 value: reg(1),
9591 },
9592 Instruction::ArrayExtend {
9593 array: reg(0),
9594 iterable: reg(2),
9595 },
9596 Instruction::LoadConst {
9597 dst: reg(3),
9598 constant: cid(3),
9599 },
9600 Instruction::GetProperty {
9601 dst: reg(0),
9602 object: reg(0),
9603 key: reg(3),
9604 },
9605 Instruction::Return { value: reg(0) },
9606 ],
9607 vec![],
9608 );
9609 let module = verified(
9610 vec![
9611 Constant::Int32(1),
9612 Constant::Int32(2),
9613 Constant::Int32(3),
9614 Constant::String(EcmaString::from_utf8("length")),
9615 ],
9616 vec![entry],
9617 );
9618 assert_eq!(run_ok(&module).value, Value::int32(3));
9619 }
9620
9621 #[test]
9622 fn array_extend_uses_sync_protocol_for_set_and_rejects_plain_object() {
9623 let module = verified(
9624 Vec::new(),
9625 vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
9626 );
9627 let mut host = TestHost;
9628 let mut machine = Machine::new(&module, &mut host, Limits::default());
9629 let set_constructor = machine.intrinsics.global("Set").unwrap();
9630 let set_prototype = machine
9631 .get_named_property(set_constructor, "prototype")
9632 .unwrap();
9633 let set = machine
9634 .allocate(HeapEntry::Collection {
9635 entries: vec![CollectionEntry {
9636 order: 0,
9637 key: Value::int32(7),
9638 value: Value::int32(7),
9639 }],
9640 next_order: 1,
9641 properties: PropertyMap::default(),
9642 prototype: Some(set_prototype),
9643 extensible: true,
9644 })
9645 .unwrap();
9646 let target = machine
9647 .allocate(HeapEntry::Array {
9648 elements: Vec::new(),
9649 properties: PropertyMap::default(),
9650 prototype: Some(machine.intrinsics.array_prototype),
9651 extensible: true,
9652 length_writable: true,
9653 })
9654 .unwrap();
9655
9656 machine.array_extend(target, set).unwrap();
9657 assert_eq!(
9658 machine.array_elements(target).unwrap(),
9659 Some(vec![Value::int32(7)])
9660 );
9661
9662 let plain_object = machine
9663 .allocate(HeapEntry::Object {
9664 properties: PropertyMap::default(),
9665 prototype: Some(machine.intrinsics.object_prototype),
9666 boxed_primitive: None,
9667 extensible: true,
9668 })
9669 .unwrap();
9670 assert!(matches!(
9671 machine.array_extend(target, plain_object),
9672 Err(EvalFailure::Throw(ThrowOrigin::TypeError {
9673 operation: "value is not iterable"
9674 }))
9675 ));
9676 }
9677
9678 #[test]
9679 fn sync_iterator_uses_symbol_method_and_caches_next() {
9680 fn iterator_identity<H: Host>(
9681 _machine: &mut Machine<'_, H>,
9682 this: Value,
9683 _args: &[Value],
9684 _constructing: bool,
9685 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9686 Ok(intrinsics::BuiltinOutcome::Value(this))
9687 }
9688
9689 fn next_getter<H: Host>(
9690 machine: &mut Machine<'_, H>,
9691 this: Value,
9692 _args: &[Value],
9693 _constructing: bool,
9694 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9695 let reads = machine.get_named_property(this, "nextReads")?;
9696 let reads = if reads == Value::int32(0) { 1 } else { 2 };
9697 machine.set_data_property(this, "nextReads", Value::int32(reads))?;
9698 Ok(intrinsics::BuiltinOutcome::Value(
9699 machine.get_named_property(this, "nextFunction")?,
9700 ))
9701 }
9702
9703 fn next_result<H: Host>(
9704 machine: &mut Machine<'_, H>,
9705 this: Value,
9706 _args: &[Value],
9707 _constructing: bool,
9708 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9709 Ok(intrinsics::BuiltinOutcome::Value(
9710 machine.get_named_property(this, "result")?,
9711 ))
9712 }
9713
9714 fn done_getter<H: Host>(
9715 machine: &mut Machine<'_, H>,
9716 this: Value,
9717 _args: &[Value],
9718 _constructing: bool,
9719 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9720 machine.set_data_property(this, "order", Value::int32(1))?;
9721 Ok(intrinsics::BuiltinOutcome::Value(Value::FALSE))
9722 }
9723
9724 fn value_getter<H: Host>(
9725 machine: &mut Machine<'_, H>,
9726 this: Value,
9727 _args: &[Value],
9728 _constructing: bool,
9729 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9730 if machine.get_named_property(this, "order")? != Value::int32(1) {
9731 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
9732 operation: "iterator value read before done",
9733 }));
9734 }
9735 Ok(intrinsics::BuiltinOutcome::Value(Value::int32(42)))
9736 }
9737
9738 let module = verified(
9739 Vec::new(),
9740 vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
9741 );
9742 let mut host = TestHost;
9743 let mut machine = Machine::new(&module, &mut host, Limits::default());
9744 let mut install = |name, handler| {
9745 let id = machine
9746 .intrinsics
9747 .builtins
9748 .register(intrinsics::BuiltinDef {
9749 name,
9750 length: 0,
9751 handler,
9752 });
9753 intrinsics::native_function(&mut machine.heap, id, name, 0)
9754 };
9755 let iterator_identity = install(
9756 "[Symbol.iterator]",
9757 iterator_identity::<TestHost> as intrinsics::BuiltinHandler<TestHost>,
9758 );
9759 let next_getter = install("get next", next_getter::<TestHost>);
9760 let next_result = install("next", next_result::<TestHost>);
9761 let done_getter = install("get done", done_getter::<TestHost>);
9762 let value_getter = install("get value", value_getter::<TestHost>);
9763 let object_prototype = machine.intrinsics.object_prototype;
9764 let result = machine
9765 .allocate(HeapEntry::Object {
9766 properties: {
9767 let mut properties = PropertyMap::default();
9768 for (key, property) in [
9769 (
9770 PropertyKey::Named(EcmaString::from_utf8("order")),
9771 Property::Data {
9772 value: Value::int32(0),
9773 writable: true,
9774 enumerable: true,
9775 configurable: true,
9776 },
9777 ),
9778 (
9779 PropertyKey::Named(EcmaString::from_utf8("done")),
9780 Property::Accessor {
9781 getter: Some(done_getter),
9782 setter: None,
9783 enumerable: true,
9784 configurable: true,
9785 },
9786 ),
9787 (
9788 PropertyKey::Named(EcmaString::from_utf8("value")),
9789 Property::Accessor {
9790 getter: Some(value_getter),
9791 setter: None,
9792 enumerable: true,
9793 configurable: true,
9794 },
9795 ),
9796 ] {
9797 properties.insert(key, property);
9798 }
9799 properties
9800 },
9801 prototype: Some(object_prototype),
9802 boxed_primitive: None,
9803 extensible: true,
9804 })
9805 .unwrap();
9806 let iterator_symbol = machine.intrinsics.builtins.symbol_iterator();
9807 let iterator_key = machine.to_property_key(iterator_symbol).unwrap();
9808 let source = machine
9809 .allocate(HeapEntry::Object {
9810 properties: {
9811 let mut properties = PropertyMap::default();
9812 for (key, property) in [
9813 (
9814 iterator_key,
9815 Property::Data {
9816 value: iterator_identity,
9817 writable: true,
9818 enumerable: false,
9819 configurable: true,
9820 },
9821 ),
9822 (
9823 PropertyKey::Named(EcmaString::from_utf8("next")),
9824 Property::Accessor {
9825 getter: Some(next_getter),
9826 setter: None,
9827 enumerable: false,
9828 configurable: true,
9829 },
9830 ),
9831 (
9832 PropertyKey::Named(EcmaString::from_utf8("nextReads")),
9833 Property::Data {
9834 value: Value::int32(0),
9835 writable: true,
9836 enumerable: true,
9837 configurable: true,
9838 },
9839 ),
9840 (
9841 PropertyKey::Named(EcmaString::from_utf8("nextFunction")),
9842 Property::Data {
9843 value: next_result,
9844 writable: true,
9845 enumerable: true,
9846 configurable: true,
9847 },
9848 ),
9849 (
9850 PropertyKey::Named(EcmaString::from_utf8("result")),
9851 Property::Data {
9852 value: result,
9853 writable: true,
9854 enumerable: true,
9855 configurable: true,
9856 },
9857 ),
9858 ] {
9859 properties.insert(key, property);
9860 }
9861 properties
9862 },
9863 prototype: Some(object_prototype),
9864 boxed_primitive: None,
9865 extensible: true,
9866 })
9867 .unwrap();
9868
9869 let iterator = machine.create_iterator(source, IteratorKind::Sync).unwrap();
9870 assert_eq!(
9871 machine.iterator_next(iterator).unwrap(),
9872 (false, Value::int32(42))
9873 );
9874 assert_eq!(
9875 machine.iterator_next(iterator).unwrap(),
9876 (false, Value::int32(42))
9877 );
9878 assert_eq!(
9879 machine.get_named_property(source, "nextReads").unwrap(),
9880 Value::int32(1)
9881 );
9882
9883 let mut completed_properties = PropertyMap::default();
9884 completed_properties.insert(
9885 PropertyKey::Named(EcmaString::from_utf8("done")),
9886 Property::Data {
9887 value: Value::TRUE,
9888 writable: true,
9889 enumerable: true,
9890 configurable: true,
9891 },
9892 );
9893 completed_properties.insert(
9894 PropertyKey::Named(EcmaString::from_utf8("value")),
9895 Property::Accessor {
9896 getter: Some(value_getter),
9897 setter: None,
9898 enumerable: true,
9899 configurable: true,
9900 },
9901 );
9902 let completed = machine
9903 .allocate(HeapEntry::Object {
9904 properties: completed_properties,
9905 prototype: Some(object_prototype),
9906 boxed_primitive: None,
9907 extensible: true,
9908 })
9909 .unwrap();
9910 machine
9911 .set_data_property(source, "result", completed)
9912 .unwrap();
9913 assert_eq!(
9914 machine.iterator_next(iterator).unwrap(),
9915 (true, Value::UNDEFINED)
9916 );
9917
9918 machine
9919 .delete_property(source, &PropertyKey::Named(EcmaString::from_utf8("next")))
9920 .unwrap();
9921 machine
9922 .set_data_property(source, "next", Value::int32(1))
9923 .unwrap();
9924 let invalid_next = machine.create_iterator(source, IteratorKind::Sync).unwrap();
9925 assert!(matches!(
9926 machine.iterator_next(invalid_next),
9927 Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
9928 ));
9929 }
9930
9931 #[test]
9932 fn object_spread_copies_own_properties() {
9933 let key = |c: u32| Instruction::LoadConst {
9935 dst: reg(3),
9936 constant: cid(c),
9937 };
9938 let module = verified(
9939 vec![
9940 Constant::String(EcmaString::from_utf8("x")),
9941 Constant::Int32(9),
9942 ],
9943 vec![function(
9944 0,
9945 4,
9946 vec![
9947 Instruction::CreateObject { dst: reg(0) },
9948 key(0),
9949 Instruction::LoadConst {
9950 dst: reg(2),
9951 constant: cid(1),
9952 },
9953 Instruction::SetProperty {
9954 object: reg(0),
9955 key: reg(3),
9956 value: reg(2),
9957 },
9958 Instruction::CreateObject { dst: reg(1) },
9959 Instruction::ObjectSpread {
9960 target: reg(1),
9961 source: reg(0),
9962 },
9963 key(0),
9964 Instruction::GetProperty {
9965 dst: reg(2),
9966 object: reg(1),
9967 key: reg(3),
9968 },
9969 Instruction::Return { value: reg(2) },
9970 ],
9971 vec![],
9972 )],
9973 );
9974 assert_eq!(run_ok(&module).value, Value::int32(9));
9975 }
9976
9977 #[test]
9978 fn object_spread_copies_enumerable_symbol_properties() {
9979 let module = verified(
9980 Vec::new(),
9981 vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
9982 );
9983 let mut host = TestHost;
9984 let mut machine = Machine::new(&module, &mut host, Limits::default());
9985 let prototype = machine.intrinsics.object_prototype;
9986 let object = |machine: &mut Machine<'_, TestHost>| {
9987 machine
9988 .allocate(HeapEntry::Object {
9989 properties: PropertyMap::default(),
9990 prototype: Some(prototype),
9991 boxed_primitive: None,
9992 extensible: true,
9993 })
9994 .unwrap()
9995 };
9996 let source = object(&mut machine);
9997 let target = object(&mut machine);
9998 let symbol = machine
9999 .allocate(HeapEntry::Symbol {
10000 description: EcmaString::from_utf8("key"),
10001 })
10002 .unwrap();
10003 let key = machine.to_property_key(symbol).unwrap();
10004 machine
10005 .set_data_property_key(source, key.clone(), Value::int32(42))
10006 .unwrap();
10007
10008 machine.object_spread(target, source).unwrap();
10009
10010 assert_eq!(
10011 machine.get_property_key(target, &key).unwrap(),
10012 Value::int32(42)
10013 );
10014 }
10015
10016 #[test]
10017 fn object_spread_rechecks_descriptors_after_getters() {
10018 fn delete_next<H: Host>(
10019 machine: &mut Machine<'_, H>,
10020 this: Value,
10021 _args: &[Value],
10022 _constructing: bool,
10023 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
10024 machine.delete_property(this, &PropertyKey::Named(EcmaString::from_utf8("next")))?;
10025 Ok(intrinsics::BuiltinOutcome::Value(Value::int32(1)))
10026 }
10027
10028 let module = verified(
10029 Vec::new(),
10030 vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
10031 );
10032 let mut host = TestHost;
10033 let mut machine = Machine::new(&module, &mut host, Limits::default());
10034 let getter_id = machine
10035 .intrinsics
10036 .builtins
10037 .register(intrinsics::BuiltinDef {
10038 name: "delete next",
10039 length: 0,
10040 handler: delete_next::<TestHost>,
10041 });
10042 let getter = intrinsics::native_function(&mut machine.heap, getter_id, "delete next", 0);
10043 let first = PropertyKey::Named(EcmaString::from_utf8("first"));
10044 let next = PropertyKey::Named(EcmaString::from_utf8("next"));
10045 let mut source_properties = PropertyMap::default();
10046 source_properties.insert(
10047 first.clone(),
10048 Property::Accessor {
10049 getter: Some(getter),
10050 setter: None,
10051 enumerable: true,
10052 configurable: true,
10053 },
10054 );
10055 source_properties.insert(
10056 next.clone(),
10057 Property::Data {
10058 value: Value::int32(2),
10059 writable: true,
10060 enumerable: true,
10061 configurable: true,
10062 },
10063 );
10064 let prototype = machine.intrinsics.object_prototype;
10065 let source = machine
10066 .allocate(HeapEntry::Object {
10067 properties: source_properties,
10068 prototype: Some(prototype),
10069 boxed_primitive: None,
10070 extensible: true,
10071 })
10072 .unwrap();
10073 let target = machine
10074 .allocate(HeapEntry::Object {
10075 properties: PropertyMap::default(),
10076 prototype: Some(prototype),
10077 boxed_primitive: None,
10078 extensible: true,
10079 })
10080 .unwrap();
10081
10082 machine.object_spread(target, source).unwrap();
10083
10084 assert_eq!(
10085 machine.get_property_key(target, &first).unwrap(),
10086 Value::int32(1)
10087 );
10088 assert!(!machine.has_own_property_key(target, &next).unwrap());
10089 }
10090
10091 #[test]
10092 fn private_names_have_distinct_identity_and_are_gettable() {
10093 let module = verified(
10095 vec![
10096 Constant::String(EcmaString::from_utf8("x")),
10097 Constant::Int32(1),
10098 Constant::Int32(2),
10099 ],
10100 vec![function(
10101 0,
10102 6,
10103 vec![
10104 Instruction::CreateObject { dst: reg(0) },
10105 Instruction::CreatePrivateName {
10106 dst: reg(1),
10107 description: cid(0),
10108 },
10109 Instruction::CreatePrivateName {
10110 dst: reg(2),
10111 description: cid(0),
10112 },
10113 Instruction::LoadConst {
10114 dst: reg(3),
10115 constant: cid(1),
10116 },
10117 Instruction::SetProperty {
10118 object: reg(0),
10119 key: reg(1),
10120 value: reg(3),
10121 },
10122 Instruction::LoadConst {
10123 dst: reg(3),
10124 constant: cid(2),
10125 },
10126 Instruction::SetProperty {
10127 object: reg(0),
10128 key: reg(2),
10129 value: reg(3),
10130 },
10131 Instruction::GetProperty {
10133 dst: reg(4),
10134 object: reg(0),
10135 key: reg(1),
10136 },
10137 Instruction::GetProperty {
10138 dst: reg(5),
10139 object: reg(0),
10140 key: reg(2),
10141 },
10142 Instruction::Binary {
10144 dst: reg(3),
10145 op: BinaryOp::StrictEqual,
10146 left: reg(1),
10147 right: reg(2),
10148 },
10149 Instruction::Return { value: reg(4) },
10150 ],
10151 vec![],
10152 )],
10153 );
10154 let execution = run_ok(&module);
10155 assert_eq!(execution.value, Value::int32(1));
10156 assert_eq!(execution.entry_registers[5], Value::int32(2));
10157 assert_eq!(execution.entry_registers[3], Value::FALSE);
10158 }
10159
10160 #[test]
10161 fn accessor_getter_is_invoked_on_property_read() {
10162 let entry = function(
10164 0,
10165 4,
10166 vec![
10167 Instruction::CreateObject { dst: reg(0) },
10168 Instruction::CreateArray { dst: reg(3) },
10169 Instruction::CreateClosure {
10170 dst: reg(1),
10171 function: FunctionId::new(1),
10172 captures: reg(3),
10173 },
10174 Instruction::LoadConst {
10175 dst: reg(2),
10176 constant: cid(0),
10177 },
10178 Instruction::DefineAccessor {
10179 object: reg(0),
10180 key: reg(2),
10181 accessor: reg(1),
10182 kind: AccessorKind::Getter,
10183 },
10184 Instruction::GetProperty {
10185 dst: reg(1),
10186 object: reg(0),
10187 key: reg(2),
10188 },
10189 Instruction::Return { value: reg(1) },
10190 ],
10191 vec![],
10192 );
10193 let getter = function(
10194 0,
10195 1,
10196 vec![
10197 Instruction::LoadConst {
10198 dst: reg(0),
10199 constant: cid(1),
10200 },
10201 Instruction::Return { value: reg(0) },
10202 ],
10203 vec![],
10204 );
10205 let module = verified(
10206 vec![
10207 Constant::String(EcmaString::from_utf8("g")),
10208 Constant::Int32(99),
10209 ],
10210 vec![entry, getter],
10211 );
10212 assert_eq!(run_ok(&module).value, Value::int32(99));
10213 }
10214
10215 #[test]
10216 fn prototype_chain_lookup_and_instanceof() {
10217 let entry = function(
10220 0,
10221 6,
10222 vec![
10223 Instruction::CreateObject { dst: reg(0) },
10225 Instruction::LoadConst {
10226 dst: reg(1),
10227 constant: cid(0),
10228 },
10229 Instruction::LoadConst {
10230 dst: reg(2),
10231 constant: cid(1),
10232 },
10233 Instruction::SetProperty {
10234 object: reg(0),
10235 key: reg(1),
10236 value: reg(2),
10237 },
10238 Instruction::CreateArray { dst: reg(4) },
10240 Instruction::CreateClosure {
10241 dst: reg(3),
10242 function: FunctionId::new(1),
10243 captures: reg(4),
10244 },
10245 Instruction::LoadConst {
10247 dst: reg(1),
10248 constant: cid(2),
10249 },
10250 Instruction::SetProperty {
10251 object: reg(3),
10252 key: reg(1),
10253 value: reg(0),
10254 },
10255 Instruction::CreateArray { dst: reg(4) },
10257 Instruction::Construct {
10258 dst: reg(0),
10259 callee: reg(3),
10260 arguments: reg(4),
10261 },
10262 Instruction::LoadConst {
10264 dst: reg(1),
10265 constant: cid(0),
10266 },
10267 Instruction::GetProperty {
10268 dst: reg(2),
10269 object: reg(0),
10270 key: reg(1),
10271 },
10272 Instruction::Binary {
10274 dst: reg(5),
10275 op: BinaryOp::InstanceOf,
10276 left: reg(0),
10277 right: reg(3),
10278 },
10279 Instruction::Return { value: reg(2) },
10280 ],
10281 vec![],
10282 );
10283 let ctor = function(0, 1, vec![Instruction::Halt], vec![]);
10284 let module = verified(
10285 vec![
10286 Constant::String(EcmaString::from_utf8("m")),
10287 Constant::Int32(5),
10288 Constant::String(EcmaString::from_utf8("prototype")),
10289 ],
10290 vec![entry, ctor],
10291 );
10292 let execution = run_ok(&module);
10293 assert_eq!(execution.value, Value::int32(5));
10294 assert_eq!(execution.entry_registers[5], Value::TRUE);
10295 }
10296
10297 #[test]
10298 fn sync_iterator_walks_array_elements() {
10299 let entry = function(
10301 0,
10302 6,
10303 vec![
10304 Instruction::CreateArray { dst: reg(0) },
10305 Instruction::LoadConst {
10306 dst: reg(1),
10307 constant: cid(0),
10308 },
10309 Instruction::ArrayPush {
10310 array: reg(0),
10311 value: reg(1),
10312 },
10313 Instruction::LoadConst {
10314 dst: reg(1),
10315 constant: cid(1),
10316 },
10317 Instruction::ArrayPush {
10318 array: reg(0),
10319 value: reg(1),
10320 },
10321 Instruction::LoadConst {
10323 dst: reg(2),
10324 constant: cid(2),
10325 },
10326 Instruction::GetIterator {
10327 dst: reg(3),
10328 src: reg(0),
10329 kind: IteratorKind::Sync,
10330 },
10331 Instruction::IteratorNext {
10333 done: reg(4),
10334 value: reg(5),
10335 iterator: reg(3),
10336 },
10337 Instruction::JumpIfTrue {
10338 condition: reg(4),
10339 target: pc(11),
10340 },
10341 Instruction::Binary {
10342 dst: reg(2),
10343 op: BinaryOp::Add,
10344 left: reg(2),
10345 right: reg(5),
10346 },
10347 Instruction::Jump { target: pc(7) },
10348 Instruction::Return { value: reg(2) },
10350 ],
10351 vec![],
10352 );
10353 let module = verified(
10354 vec![Constant::Int32(10), Constant::Int32(20), Constant::Int32(0)],
10355 vec![entry],
10356 );
10357 assert_eq!(run_ok(&module).value, Value::int32(30));
10358 }
10359
10360 #[test]
10361 fn keys_iterator_enumerates_own_object_keys() {
10362 let entry = function(
10364 0,
10365 6,
10366 vec![
10367 Instruction::CreateObject { dst: reg(0) },
10368 Instruction::LoadConst {
10369 dst: reg(1),
10370 constant: cid(0),
10371 },
10372 Instruction::LoadConst {
10373 dst: reg(2),
10374 constant: cid(1),
10375 },
10376 Instruction::SetProperty {
10377 object: reg(0),
10378 key: reg(1),
10379 value: reg(2),
10380 },
10381 Instruction::GetIterator {
10382 dst: reg(3),
10383 src: reg(0),
10384 kind: IteratorKind::Keys,
10385 },
10386 Instruction::IteratorNext {
10387 done: reg(4),
10388 value: reg(5),
10389 iterator: reg(3),
10390 },
10391 Instruction::Return { value: reg(5) },
10392 ],
10393 vec![],
10394 );
10395 let module = verified(
10396 vec![
10397 Constant::String(EcmaString::from_utf8("a")),
10398 Constant::Int32(1),
10399 ],
10400 vec![entry],
10401 );
10402 let execution = run_ok(&module);
10403 let key = execution.value;
10405 assert_eq!(execution.entry_registers[4], Value::FALSE);
10408 assert_ne!(key, Value::UNDEFINED);
10409 }
10410
10411 #[test]
10412 fn async_iterator_steps_like_sync() {
10413 let entry = function(
10414 0,
10415 5,
10416 vec![
10417 Instruction::CreateArray { dst: reg(0) },
10418 Instruction::LoadConst {
10419 dst: reg(1),
10420 constant: cid(0),
10421 },
10422 Instruction::ArrayPush {
10423 array: reg(0),
10424 value: reg(1),
10425 },
10426 Instruction::GetIterator {
10427 dst: reg(2),
10428 src: reg(0),
10429 kind: IteratorKind::Async,
10430 },
10431 Instruction::IteratorNext {
10432 done: reg(3),
10433 value: reg(4),
10434 iterator: reg(2),
10435 },
10436 Instruction::Return { value: reg(4) },
10437 ],
10438 vec![],
10439 );
10440 let module = verified(vec![Constant::Int32(8)], vec![entry]);
10441 let execution = run_ok(&module);
10442 assert_eq!(execution.value, Value::int32(8));
10443 assert_eq!(execution.entry_registers[3], Value::FALSE);
10444 }
10445
10446 #[test]
10447 fn globals_store_load_and_typeof_undeclared() {
10448 let entry = function(
10451 0,
10452 3,
10453 vec![
10454 Instruction::LoadConst {
10455 dst: reg(0),
10456 constant: cid(2),
10457 },
10458 Instruction::StoreGlobal {
10459 name: cid(0),
10460 value: reg(0),
10461 },
10462 Instruction::TypeOfGlobal {
10463 dst: reg(1),
10464 name: cid(1),
10465 },
10466 Instruction::TypeOfGlobal {
10467 dst: reg(2),
10468 name: cid(0),
10469 },
10470 Instruction::LoadGlobal {
10471 dst: reg(0),
10472 name: cid(0),
10473 },
10474 Instruction::Return { value: reg(0) },
10475 ],
10476 vec![],
10477 );
10478 let module = verified(
10479 vec![
10480 Constant::String(EcmaString::from_utf8("x")),
10481 Constant::String(EcmaString::from_utf8("y")),
10482 Constant::Int32(5),
10483 ],
10484 vec![entry],
10485 );
10486 assert_eq!(run_ok(&module).value, Value::int32(5));
10487 }
10488
10489 #[test]
10490 fn create_cell_throws_reference_error_before_initialization() {
10491 let module = verified(
10492 vec![Constant::Int32(0)],
10493 vec![function(
10494 0,
10495 3,
10496 vec![
10497 Instruction::CreateCell { dst: reg(0) },
10498 Instruction::LoadConst {
10499 dst: reg(1),
10500 constant: cid(0),
10501 },
10502 Instruction::GetProperty {
10503 dst: reg(2),
10504 object: reg(0),
10505 key: reg(1),
10506 },
10507 Instruction::Return { value: reg(2) },
10508 ],
10509 vec![],
10510 )],
10511 );
10512 let mut host = TestHost;
10513 let error = Machine::new(&module, &mut host, Limits::default())
10514 .run()
10515 .expect_err("uninitialized cell read throws");
10516 assert!(matches!(
10517 error.kind,
10518 RuntimeErrorKind::UncaughtThrow {
10519 origin: ThrowOrigin::ReferenceError { .. },
10520 ..
10521 }
10522 ));
10523 }
10524
10525 #[test]
10526 fn create_cell_can_be_initialized_to_undefined() {
10527 let module = verified(
10528 vec![Constant::Int32(0), Constant::Undefined],
10529 vec![function(
10530 0,
10531 4,
10532 vec![
10533 Instruction::CreateCell { dst: reg(0) },
10534 Instruction::LoadConst {
10535 dst: reg(1),
10536 constant: cid(0),
10537 },
10538 Instruction::LoadConst {
10539 dst: reg(2),
10540 constant: cid(1),
10541 },
10542 Instruction::SetProperty {
10543 object: reg(0),
10544 key: reg(1),
10545 value: reg(2),
10546 },
10547 Instruction::GetProperty {
10548 dst: reg(3),
10549 object: reg(0),
10550 key: reg(1),
10551 },
10552 Instruction::Return { value: reg(3) },
10553 ],
10554 vec![],
10555 )],
10556 );
10557 let mut host = TestHost;
10558 let execution = Machine::new(&module, &mut host, Limits::default())
10559 .run()
10560 .expect("explicit undefined initializes the cell");
10561 assert_eq!(execution.value, Value::UNDEFINED);
10562 }
10563
10564 #[test]
10565 fn load_undeclared_global_throws_reference_error() {
10566 let module = verified(
10567 vec![Constant::String(EcmaString::from_utf8("missing"))],
10568 vec![function(
10569 0,
10570 2,
10571 vec![
10572 Instruction::LoadGlobal {
10573 dst: reg(0),
10574 name: cid(0),
10575 },
10576 Instruction::Halt,
10577 Instruction::Return { value: reg(1) },
10578 ],
10579 vec![ExceptionHandler {
10580 start: pc(0),
10581 end: pc(1),
10582 handler: pc(2),
10583 catch_register: reg(1),
10584 }],
10585 )],
10586 );
10587 let mut host = TestHost;
10588 let execution = Machine::new(&module, &mut host, Limits::default())
10591 .run()
10592 .unwrap();
10593 assert_eq!(execution.value, Value::UNDEFINED);
10594 }
10595
10596 #[test]
10597 fn uncaught_reference_error_reports_origin() {
10598 let module = verified(
10599 vec![Constant::String(EcmaString::from_utf8("missing"))],
10600 vec![function(
10601 0,
10602 1,
10603 vec![
10604 Instruction::LoadGlobal {
10605 dst: reg(0),
10606 name: cid(0),
10607 },
10608 Instruction::Return { value: reg(0) },
10609 ],
10610 vec![],
10611 )],
10612 );
10613 let mut host = TestHost;
10614 let error = Machine::new(&module, &mut host, Limits::default())
10615 .run()
10616 .unwrap_err();
10617 assert_eq!(error.pc, pc(0));
10618 assert!(matches!(
10619 error.kind,
10620 RuntimeErrorKind::UncaughtThrow {
10621 origin: ThrowOrigin::ReferenceError { .. },
10622 ..
10623 }
10624 ));
10625 }
10626
10627 fn assert_uri_error(global: &str, argument: EcmaString) {
10628 let module = verified(
10629 vec![
10630 Constant::String(EcmaString::from_utf8(global)),
10631 Constant::String(argument),
10632 Constant::Undefined,
10633 ],
10634 vec![function(
10635 0,
10636 5,
10637 vec![
10638 Instruction::LoadGlobal {
10639 dst: reg(0),
10640 name: cid(0),
10641 },
10642 Instruction::LoadConst {
10643 dst: reg(1),
10644 constant: cid(1),
10645 },
10646 Instruction::LoadConst {
10647 dst: reg(2),
10648 constant: cid(2),
10649 },
10650 Instruction::CreateArray { dst: reg(3) },
10651 Instruction::ArrayPush {
10652 array: reg(3),
10653 value: reg(1),
10654 },
10655 Instruction::Call {
10656 dst: reg(4),
10657 callee: reg(0),
10658 this_value: reg(2),
10659 arguments: reg(3),
10660 },
10661 Instruction::Return { value: reg(4) },
10662 ],
10663 Vec::new(),
10664 )],
10665 );
10666 let mut host = TestHost;
10667 let error = Machine::new(&module, &mut host, Limits::default())
10668 .run()
10669 .unwrap_err();
10670 assert_eq!(error.pc, pc(5));
10671 assert!(matches!(
10672 error.kind,
10673 RuntimeErrorKind::UncaughtThrow {
10674 origin: ThrowOrigin::UriError {
10675 operation: "URI malformed"
10676 },
10677 ..
10678 }
10679 ));
10680 }
10681
10682 #[test]
10683 fn uri_builtins_report_uri_error() {
10684 for (global, argument) in [
10685 ("encodeURIComponent", EcmaString::from_units(&[0xd800])),
10686 ("decodeURIComponent", EcmaString::from_utf8("%")),
10687 ("decodeURIComponent", EcmaString::from_utf8("%GG")),
10688 ("decodeURIComponent", EcmaString::from_utf8("%FF")),
10689 ("decodeURIComponent", EcmaString::from_utf8("%80")),
10690 ("decodeURIComponent", EcmaString::from_utf8("%C0%80")),
10691 ("decodeURIComponent", EcmaString::from_utf8("%E2%82")),
10692 ("decodeURIComponent", EcmaString::from_utf8("%ED%A0%80")),
10693 ("decodeURIComponent", EcmaString::from_utf8("%F4%90%80%80")),
10694 (
10695 "decodeURIComponent",
10696 EcmaString::from_utf8("%F8%80%80%80%80"),
10697 ),
10698 ] {
10699 assert_uri_error(global, argument);
10700 }
10701 }
10702
10703 fn assert_uri_decode(argument: EcmaString, expected: EcmaString) {
10704 let module = verified(
10705 vec![
10706 Constant::String(EcmaString::from_utf8("decodeURIComponent")),
10707 Constant::String(argument),
10708 Constant::Undefined,
10709 Constant::String(expected),
10710 ],
10711 vec![function(
10712 0,
10713 7,
10714 vec![
10715 Instruction::LoadGlobal {
10716 dst: reg(0),
10717 name: cid(0),
10718 },
10719 Instruction::LoadConst {
10720 dst: reg(1),
10721 constant: cid(1),
10722 },
10723 Instruction::LoadConst {
10724 dst: reg(2),
10725 constant: cid(2),
10726 },
10727 Instruction::CreateArray { dst: reg(3) },
10728 Instruction::ArrayPush {
10729 array: reg(3),
10730 value: reg(1),
10731 },
10732 Instruction::Call {
10733 dst: reg(4),
10734 callee: reg(0),
10735 this_value: reg(2),
10736 arguments: reg(3),
10737 },
10738 Instruction::LoadConst {
10739 dst: reg(5),
10740 constant: cid(3),
10741 },
10742 Instruction::Binary {
10743 dst: reg(6),
10744 op: BinaryOp::StrictEqual,
10745 left: reg(4),
10746 right: reg(5),
10747 },
10748 Instruction::Return { value: reg(6) },
10749 ],
10750 Vec::new(),
10751 )],
10752 );
10753 let mut host = TestHost;
10754 let execution = Machine::new(&module, &mut host, Limits::default())
10755 .run()
10756 .unwrap();
10757 assert_eq!(execution.value, Value::TRUE);
10758 }
10759
10760 #[test]
10761 fn decode_uri_component_preserves_units_and_decodes_utf8() {
10762 let exact = EcmaString::from_units(&[0xd800, 0x61, 0xdfff]);
10763 for (argument, expected) in [
10764 (exact.clone(), exact),
10765 (EcmaString::from_utf8("%2F"), EcmaString::from_utf8("/")),
10766 (
10767 EcmaString::from_utf8("%F0%9F%98%80"),
10768 EcmaString::from_utf8("😀"),
10769 ),
10770 (
10771 EcmaString::from_utf8("%E4%B8%ADA"),
10772 EcmaString::from_utf8("ä¸A"),
10773 ),
10774 (EcmaString::from_utf8("%00"), EcmaString::from_units(&[0])),
10775 ] {
10776 assert_uri_decode(argument, expected);
10777 }
10778 }
10779
10780 #[test]
10781 fn regexp_is_object_with_source_and_flags() {
10782 let module = verified(
10784 vec![
10785 Constant::String(EcmaString::from_utf8("ab")),
10786 Constant::String(EcmaString::from_utf8("gi")),
10787 Constant::String(EcmaString::from_utf8("source")),
10788 Constant::String(EcmaString::from_utf8("global")),
10789 ],
10790 vec![function(
10791 0,
10792 4,
10793 vec![
10794 Instruction::CreateRegExp {
10795 dst: reg(0),
10796 pattern: cid(0),
10797 flags: cid(1),
10798 },
10799 Instruction::LoadConst {
10800 dst: reg(1),
10801 constant: cid(3),
10802 },
10803 Instruction::GetProperty {
10804 dst: reg(2),
10805 object: reg(0),
10806 key: reg(1),
10807 },
10808 Instruction::Unary {
10809 dst: reg(3),
10810 op: UnaryOp::TypeOf,
10811 operand: reg(0),
10812 },
10813 Instruction::Return { value: reg(2) },
10814 ],
10815 vec![],
10816 )],
10817 );
10818 let execution = run_ok(&module);
10819 assert_eq!(execution.value, Value::TRUE);
10821 }
10822
10823 #[test]
10824 fn this_and_new_target_are_frame_owned() {
10825 let entry = function(
10827 0,
10828 4,
10829 vec![
10830 Instruction::CreateObject { dst: reg(0) },
10831 Instruction::CreateArray { dst: reg(3) },
10832 Instruction::CreateClosure {
10833 dst: reg(1),
10834 function: FunctionId::new(1),
10835 captures: reg(3),
10836 },
10837 Instruction::CreateArray { dst: reg(2) },
10838 Instruction::Call {
10839 dst: reg(0),
10840 callee: reg(1),
10841 this_value: reg(0),
10842 arguments: reg(2),
10843 },
10844 Instruction::Return { value: reg(0) },
10845 ],
10846 vec![],
10847 );
10848 let callee = function(
10851 0,
10852 2,
10853 vec![
10854 Instruction::LoadNewTarget { dst: reg(0) },
10855 Instruction::Unary {
10856 dst: reg(1),
10857 op: UnaryOp::TypeOf,
10858 operand: reg(0),
10859 },
10860 Instruction::Return { value: reg(1) },
10861 ],
10862 vec![],
10863 );
10864 let module = verified(vec![], vec![entry, callee]);
10865 let execution = run_ok(&module);
10866 assert_ne!(execution.value, Value::UNDEFINED);
10870 }
10871
10872 #[test]
10873 fn new_target_is_constructor_during_construct() {
10874 let entry = function(
10877 0,
10878 4,
10879 vec![
10880 Instruction::CreateArray { dst: reg(3) },
10881 Instruction::CreateClosure {
10882 dst: reg(0),
10883 function: FunctionId::new(1),
10884 captures: reg(3),
10885 },
10886 Instruction::CreateObject { dst: reg(1) },
10888 Instruction::LoadConst {
10889 dst: reg(2),
10890 constant: cid(0),
10891 },
10892 Instruction::SetProperty {
10893 object: reg(0),
10894 key: reg(2),
10895 value: reg(1),
10896 },
10897 Instruction::CreateArray { dst: reg(3) },
10898 Instruction::Construct {
10899 dst: reg(1),
10900 callee: reg(0),
10901 arguments: reg(3),
10902 },
10903 Instruction::LoadConst {
10905 dst: reg(2),
10906 constant: cid(1),
10907 },
10908 Instruction::GetProperty {
10909 dst: reg(3),
10910 object: reg(1),
10911 key: reg(2),
10912 },
10913 Instruction::Binary {
10914 dst: reg(3),
10915 op: BinaryOp::StrictEqual,
10916 left: reg(3),
10917 right: reg(0),
10918 },
10919 Instruction::Return { value: reg(3) },
10920 ],
10921 vec![],
10922 );
10923 let ctor = function(
10924 0,
10925 3,
10926 vec![
10927 Instruction::LoadNewTarget { dst: reg(0) },
10928 Instruction::LoadThis { dst: reg(1) },
10929 Instruction::LoadConst {
10930 dst: reg(2),
10931 constant: cid(1),
10932 },
10933 Instruction::SetProperty {
10934 object: reg(1),
10935 key: reg(2),
10936 value: reg(0),
10937 },
10938 Instruction::Halt,
10939 ],
10940 vec![],
10941 );
10942 let module = verified(
10943 vec![
10944 Constant::String(EcmaString::from_utf8("prototype")),
10945 Constant::String(EcmaString::from_utf8("nt")),
10946 ],
10947 vec![entry, ctor],
10948 );
10949 assert_eq!(run_ok(&module).value, Value::TRUE);
10950 }
10951
10952 #[test]
10953 fn arguments_object_reflects_passed_values() {
10954 let entry = function(
10956 0,
10957 4,
10958 vec![
10959 Instruction::CreateArray { dst: reg(3) },
10960 Instruction::CreateClosure {
10961 dst: reg(0),
10962 function: FunctionId::new(1),
10963 captures: reg(3),
10964 },
10965 Instruction::CreateArray { dst: reg(2) },
10967 Instruction::LoadConst {
10968 dst: reg(1),
10969 constant: cid(0),
10970 },
10971 Instruction::ArrayPush {
10972 array: reg(2),
10973 value: reg(1),
10974 },
10975 Instruction::Call {
10976 dst: reg(0),
10977 callee: reg(0),
10978 this_value: reg(1),
10979 arguments: reg(2),
10980 },
10981 Instruction::Return { value: reg(0) },
10982 ],
10983 vec![],
10984 );
10985 let callee = function(
10986 0,
10987 2,
10988 vec![
10989 Instruction::LoadArguments { dst: reg(0) },
10990 Instruction::LoadConst {
10991 dst: reg(1),
10992 constant: cid(1),
10993 },
10994 Instruction::GetProperty {
10995 dst: reg(0),
10996 object: reg(0),
10997 key: reg(1),
10998 },
10999 Instruction::Return { value: reg(0) },
11000 ],
11001 vec![],
11002 );
11003 let module = verified(
11004 vec![
11005 Constant::Int32(42),
11006 Constant::String(EcmaString::from_utf8("0")),
11007 ],
11008 vec![entry, callee],
11009 );
11010 assert_eq!(run_ok(&module).value, Value::int32(42));
11011 }
11012
11013 #[test]
11014 fn catch_register_receives_exact_thrown_value() {
11015 let module = verified(
11016 vec![Constant::Int32(9)],
11017 vec![function(
11018 0,
11019 2,
11020 vec![
11021 Instruction::LoadConst {
11022 dst: reg(0),
11023 constant: cid(0),
11024 },
11025 Instruction::Throw { value: reg(0) },
11026 Instruction::Return { value: reg(1) },
11027 ],
11028 vec![ExceptionHandler {
11029 start: pc(1),
11030 end: pc(2),
11031 handler: pc(2),
11032 catch_register: reg(1),
11033 }],
11034 )],
11035 );
11036 assert_eq!(run_ok(&module).value, Value::int32(9));
11037 }
11038
11039 #[test]
11040 fn native_callback_throw_is_caught_at_outer_call_site() {
11041 let entry = function(
11042 0,
11043 9,
11044 vec![
11045 Instruction::CreateArray { dst: reg(0) },
11046 Instruction::LoadConst {
11047 dst: reg(1),
11048 constant: cid(0),
11049 },
11050 Instruction::ArrayPush {
11051 array: reg(0),
11052 value: reg(1),
11053 },
11054 Instruction::CreateArray { dst: reg(2) },
11055 Instruction::CreateClosure {
11056 dst: reg(3),
11057 function: FunctionId::new(1),
11058 captures: reg(2),
11059 },
11060 Instruction::LoadConst {
11061 dst: reg(4),
11062 constant: cid(1),
11063 },
11064 Instruction::GetProperty {
11065 dst: reg(5),
11066 object: reg(0),
11067 key: reg(4),
11068 },
11069 Instruction::CreateArray { dst: reg(6) },
11070 Instruction::ArrayPush {
11071 array: reg(6),
11072 value: reg(3),
11073 },
11074 Instruction::Call {
11075 dst: reg(7),
11076 callee: reg(5),
11077 this_value: reg(0),
11078 arguments: reg(6),
11079 },
11080 Instruction::Halt,
11081 Instruction::Return { value: reg(8) },
11082 ],
11083 vec![ExceptionHandler {
11084 start: pc(9),
11085 end: pc(10),
11086 handler: pc(11),
11087 catch_register: reg(8),
11088 }],
11089 );
11090 let callback = closure_function(
11091 0,
11092 0,
11093 1,
11094 vec![
11095 Instruction::LoadConst {
11096 dst: reg(0),
11097 constant: cid(0),
11098 },
11099 Instruction::Throw { value: reg(0) },
11100 ],
11101 );
11102 let module = verified(
11103 vec![
11104 Constant::Int32(7),
11105 Constant::String(EcmaString::from_utf8("map")),
11106 ],
11107 vec![entry, callback],
11108 );
11109
11110 assert_eq!(run_ok(&module).value, Value::int32(7));
11111 }
11112
11113 #[test]
11114 fn native_callback_throw_uncaught_at_outer_call_site() {
11115 let entry = function(
11116 0,
11117 9,
11118 vec![
11119 Instruction::CreateArray { dst: reg(0) },
11120 Instruction::LoadConst {
11121 dst: reg(1),
11122 constant: cid(0),
11123 },
11124 Instruction::ArrayPush {
11125 array: reg(0),
11126 value: reg(1),
11127 },
11128 Instruction::CreateArray { dst: reg(2) },
11129 Instruction::CreateClosure {
11130 dst: reg(3),
11131 function: FunctionId::new(1),
11132 captures: reg(2),
11133 },
11134 Instruction::LoadConst {
11135 dst: reg(4),
11136 constant: cid(1),
11137 },
11138 Instruction::GetProperty {
11139 dst: reg(5),
11140 object: reg(0),
11141 key: reg(4),
11142 },
11143 Instruction::CreateArray { dst: reg(6) },
11144 Instruction::ArrayPush {
11145 array: reg(6),
11146 value: reg(3),
11147 },
11148 Instruction::Call {
11149 dst: reg(7),
11150 callee: reg(5),
11151 this_value: reg(0),
11152 arguments: reg(6),
11153 },
11154 Instruction::Halt,
11155 ],
11156 Vec::new(),
11157 );
11158 let callback = closure_function(
11159 0,
11160 0,
11161 1,
11162 vec![
11163 Instruction::LoadConst {
11164 dst: reg(0),
11165 constant: cid(0),
11166 },
11167 Instruction::Throw { value: reg(0) },
11168 ],
11169 );
11170 let simple = closure_function(
11171 0,
11172 0,
11173 1,
11174 vec![
11175 Instruction::LoadConst {
11176 dst: reg(0),
11177 constant: cid(2),
11178 },
11179 Instruction::Return { value: reg(0) },
11180 ],
11181 );
11182 let module = verified(
11183 vec![
11184 Constant::Int32(7),
11185 Constant::String(EcmaString::from_utf8("map")),
11186 Constant::Int32(42),
11187 ],
11188 vec![entry, callback, simple],
11189 );
11190
11191 let mut host = TestHost;
11192 let mut machine = Machine::new(&module, &mut host, Limits::default());
11193 let error = machine.run_loop(0).unwrap_err();
11194 assert_eq!(
11195 error.kind,
11196 RuntimeErrorKind::UncaughtThrow {
11197 value: Value::int32(7),
11198 origin: ThrowOrigin::Bytecode,
11199 }
11200 );
11201 assert!(machine.callback_boundaries.is_empty());
11202 assert!(machine.frames.is_empty());
11203 assert_eq!(machine.live_registers, 0);
11204
11205 let callee = machine
11206 .allocate(HeapEntry::Function {
11207 module: ModuleId::new(0),
11208 function: FunctionId::new(2),
11209 captures: Vec::new(),
11210 properties: PropertyMap::default(),
11211 prototype: Some(machine.intrinsics.function_prototype),
11212 extensible: true,
11213 })
11214 .unwrap();
11215 assert_eq!(
11216 machine.call_value(callee, Value::UNDEFINED, &[]).unwrap(),
11217 Value::int32(42)
11218 );
11219 }
11220
11221 #[test]
11222 fn callee_throw_unwinds_to_call_site_handler() {
11223 let entry = function(
11224 0,
11225 4,
11226 vec![
11227 Instruction::CreateArray { dst: reg(3) },
11228 Instruction::CreateClosure {
11229 dst: reg(0),
11230 function: FunctionId::new(1),
11231 captures: reg(3),
11232 },
11233 Instruction::CreateArray { dst: reg(1) },
11234 Instruction::Call {
11235 dst: reg(2),
11236 callee: reg(0),
11237 this_value: reg(1),
11238 arguments: reg(1),
11239 },
11240 Instruction::Halt,
11241 Instruction::Return { value: reg(3) },
11242 ],
11243 vec![ExceptionHandler {
11244 start: pc(3),
11245 end: pc(4),
11246 handler: pc(5),
11247 catch_register: reg(3),
11248 }],
11249 );
11250 let callee = function(
11251 0,
11252 1,
11253 vec![
11254 Instruction::LoadConst {
11255 dst: reg(0),
11256 constant: cid(0),
11257 },
11258 Instruction::Throw { value: reg(0) },
11259 ],
11260 vec![],
11261 );
11262 let module = verified(vec![Constant::Int32(7)], vec![entry, callee]);
11263 assert_eq!(run_ok(&module).value, Value::int32(7));
11264 }
11265
11266 #[test]
11267 fn heap_and_register_limits_fail_before_unbounded_growth() {
11268 let module = verified(
11269 vec![],
11270 vec![function(
11271 0,
11272 2,
11273 vec![
11274 Instruction::CreateObject { dst: reg(0) },
11275 Instruction::CreateObject { dst: reg(1) },
11276 Instruction::Halt,
11277 ],
11278 vec![],
11279 )],
11280 );
11281 let mut host = TestHost;
11282 let error = Machine::new(
11283 &module,
11284 &mut host,
11285 Limits {
11286 max_heap_slots: 1,
11287 ..Limits::default()
11288 },
11289 )
11290 .run()
11291 .unwrap_err();
11292 assert_eq!(error.pc, pc(1));
11293 assert_eq!(
11294 error.kind,
11295 RuntimeErrorKind::HeapSlotLimitExceeded { limit: 1 }
11296 );
11297
11298 let mut host = TestHost;
11299 let error = Machine::new(
11300 &module,
11301 &mut host,
11302 Limits {
11303 max_total_registers: 1,
11304 ..Limits::default()
11305 },
11306 )
11307 .run()
11308 .unwrap_err();
11309 assert_eq!(
11310 error.kind,
11311 RuntimeErrorKind::RegisterLimitExceeded { limit: 1 }
11312 );
11313 }
11314
11315 #[test]
11316 fn argument_array_length_limit_is_enforced() {
11317 let entry = function(
11318 0,
11319 4,
11320 vec![
11321 Instruction::CreateArray { dst: reg(3) },
11322 Instruction::CreateClosure {
11323 dst: reg(0),
11324 function: FunctionId::new(1),
11325 captures: reg(3),
11326 },
11327 Instruction::CreateArray { dst: reg(2) },
11328 Instruction::LoadConst {
11329 dst: reg(1),
11330 constant: cid(0),
11331 },
11332 Instruction::ArrayPush {
11333 array: reg(2),
11334 value: reg(1),
11335 },
11336 Instruction::Call {
11337 dst: reg(0),
11338 callee: reg(0),
11339 this_value: reg(1),
11340 arguments: reg(2),
11341 },
11342 Instruction::Halt,
11343 ],
11344 vec![],
11345 );
11346 let callee = function(1, 1, vec![Instruction::Return { value: reg(0) }], vec![]);
11347 let module = verified(vec![Constant::Int32(1)], vec![entry, callee]);
11348 let mut host = TestHost;
11349 let error = Machine::new(
11350 &module,
11351 &mut host,
11352 Limits {
11353 max_argument_count: 0,
11354 ..Limits::default()
11355 },
11356 )
11357 .run()
11358 .unwrap_err();
11359 assert_eq!(
11360 error.kind,
11361 RuntimeErrorKind::ArgumentLimitExceeded {
11362 limit: 0,
11363 requested: 1
11364 }
11365 );
11366 }
11367
11368 #[test]
11369 fn u32_registers_and_instruction_pcs_do_not_truncate_at_127() {
11370 let mut code = vec![Instruction::LoadConst {
11371 dst: reg(0),
11372 constant: cid(0),
11373 }];
11374 for register in 1..=199 {
11375 code.push(Instruction::Move {
11376 dst: reg(register),
11377 src: reg(register - 1),
11378 });
11379 }
11380 code.push(Instruction::Return { value: reg(199) });
11381 let module = verified(
11382 vec![Constant::Number(NumberBits::from_f64(3.5))],
11383 vec![function(0, 200, code, vec![])],
11384 );
11385 let execution = run_ok(&module);
11386 assert_eq!(execution.value, Value::number(3.5));
11387 assert_eq!(execution.entry_registers[199], Value::number(3.5));
11388 }
11389
11390 #[test]
11391 fn construct_returned_object_overrides_default_instance() {
11392 let entry = function(
11394 0,
11395 3,
11396 vec![
11397 Instruction::CreateArray { dst: reg(2) },
11398 Instruction::CreateClosure {
11399 dst: reg(0),
11400 function: FunctionId::new(1),
11401 captures: reg(2),
11402 },
11403 Instruction::CreateArray { dst: reg(2) },
11404 Instruction::Construct {
11405 dst: reg(1),
11406 callee: reg(0),
11407 arguments: reg(2),
11408 },
11409 Instruction::LoadConst {
11411 dst: reg(0),
11412 constant: cid(0),
11413 },
11414 Instruction::GetProperty {
11415 dst: reg(2),
11416 object: reg(1),
11417 key: reg(0),
11418 },
11419 Instruction::Return { value: reg(2) },
11420 ],
11421 vec![],
11422 );
11423 let returns_object = function(
11424 0,
11425 3,
11426 vec![
11427 Instruction::CreateObject { dst: reg(0) },
11428 Instruction::LoadConst {
11429 dst: reg(1),
11430 constant: cid(0),
11431 },
11432 Instruction::LoadConst {
11433 dst: reg(2),
11434 constant: cid(1),
11435 },
11436 Instruction::SetProperty {
11437 object: reg(0),
11438 key: reg(1),
11439 value: reg(2),
11440 },
11441 Instruction::Return { value: reg(0) },
11442 ],
11443 vec![],
11444 );
11445 let module = verified(
11446 vec![
11447 Constant::String(EcmaString::from_utf8("marker")),
11448 Constant::Int32(5),
11449 ],
11450 vec![entry, returns_object],
11451 );
11452 assert_eq!(run_ok(&module).value, Value::int32(5));
11453 }
11454
11455 #[test]
11456 fn ecmascript_number_formatting_is_shortest_round_trip() {
11457 let cases = [
11458 (0.1 + 0.2, "0.30000000000000004"),
11459 (1e21, "1e+21"),
11460 (-0.0, "0"),
11461 (1.0 / 3.0, "0.3333333333333333"),
11462 (1e-6, "0.000001"),
11463 (1e-7, "1e-7"),
11464 ];
11465 for (number, expected) in cases {
11466 assert_eq!(
11467 Machine::<TestHost>::ordinary_number_to_string(number),
11468 expected
11469 );
11470 }
11471 }
11472
11473 #[test]
11474 fn own_keys_put_indices_before_insertion_ordered_strings() {
11475 let module = verified(
11476 Vec::new(),
11477 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11478 );
11479 let mut host = TestHost;
11480 let mut machine = Machine::new(&module, &mut host, Limits::default());
11481 let object = machine
11482 .allocate(HeapEntry::Object {
11483 properties: PropertyMap::default(),
11484 prototype: Some(machine.intrinsics.object_prototype),
11485 boxed_primitive: None,
11486 extensible: true,
11487 })
11488 .unwrap();
11489 let index = machine.runtime_slot(object).unwrap().unwrap();
11490 for (key, value) in [("b", 1), ("2", 2), ("a", 3), ("1", 4)] {
11491 machine
11492 .set_own_data(
11493 index,
11494 PropertyKey::Named(EcmaString::from_utf8(key)),
11495 Value::int32(value),
11496 )
11497 .unwrap();
11498 }
11499 assert_eq!(
11500 machine.enumerable_keys(object).unwrap(),
11501 ["1", "2", "b", "a"].map(EcmaString::from_utf8)
11502 );
11503 }
11504
11505 #[test]
11506 fn object_prototype_to_string_uses_realm_tags() {
11507 let module = verified(
11508 Vec::new(),
11509 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11510 );
11511 let mut host = TestHost;
11512 let mut machine = Machine::new(&module, &mut host, Limits::default());
11513 let array = machine
11514 .allocate(HeapEntry::Array {
11515 elements: Vec::new(),
11516 properties: PropertyMap::default(),
11517 prototype: Some(machine.intrinsics.array_prototype),
11518 extensible: true,
11519 length_writable: true,
11520 })
11521 .unwrap();
11522 let object = machine
11523 .allocate(HeapEntry::Object {
11524 properties: PropertyMap::default(),
11525 prototype: Some(machine.intrinsics.object_prototype),
11526 boxed_primitive: None,
11527 extensible: true,
11528 })
11529 .unwrap();
11530 let function = machine.intrinsics.global("Object").unwrap();
11531 let to_string = machine.intrinsics.object_to_string();
11532 for (value, expected) in [
11533 (Value::UNDEFINED, "[object Undefined]"),
11534 (Value::NULL, "[object Null]"),
11535 (Value::TRUE, "[object Boolean]"),
11536 (array, "[object Array]"),
11537 (object, "[object Object]"),
11538 (function, "[object Function]"),
11539 ] {
11540 let tag = machine.call_value(to_string, value, &[]).unwrap();
11541 assert!(
11542 machine
11543 .string_text(tag)
11544 .is_some_and(|text| text.eq_ascii(expected))
11545 );
11546 }
11547 }
11548
11549 #[derive(Default)]
11550 struct CapabilityHost {
11551 stdout: Vec<u8>,
11552 stderr: Vec<u8>,
11553 env: BTreeMap<String, String>,
11554 }
11555
11556 impl Host for CapabilityHost {
11557 fn write_stdout(&mut self, bytes: &[u8]) {
11558 self.stdout.extend_from_slice(bytes);
11559 }
11560
11561 fn write_stderr(&mut self, bytes: &[u8]) {
11562 self.stderr.extend_from_slice(bytes);
11563 }
11564
11565 fn env(&self, name: &str) -> Option<&str> {
11566 self.env.get(name).map(String::as_str)
11567 }
11568
11569 fn set_env(&mut self, name: &str, value: &str) {
11570 self.env.insert(name.to_owned(), value.to_owned());
11571 }
11572
11573 fn delete_env(&mut self, name: &str) -> bool {
11574 self.env.remove(name).is_some()
11575 }
11576 }
11577
11578 #[test]
11579 fn console_formats_node_value_shapes_byte_exactly() {
11580 let module = verified(
11581 Vec::new(),
11582 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11583 );
11584 let mut host = CapabilityHost::default();
11585 {
11586 let mut machine = Machine::new(&module, &mut host, Limits::default());
11587 let console = machine.intrinsics.global("console").unwrap();
11588 let log = machine.get_named_property(console, "log").unwrap();
11589 let string = machine
11590 .allocate(HeapEntry::String(EcmaString::from_utf8("hello")))
11591 .unwrap();
11592 let array_string = machine
11593 .allocate(HeapEntry::String(EcmaString::from_utf8("x")))
11594 .unwrap();
11595 let array = machine
11596 .allocate(HeapEntry::Array {
11597 elements: vec![Value::int32(1), array_string],
11598 properties: PropertyMap::default(),
11599 prototype: Some(machine.intrinsics.array_prototype),
11600 extensible: true,
11601 length_writable: true,
11602 })
11603 .unwrap();
11604 let mut inner_properties = PropertyMap::default();
11605 inner_properties.insert(
11606 PropertyKey::Named(EcmaString::from_utf8("answer")),
11607 Property::Data {
11608 value: Value::int32(42),
11609 writable: true,
11610 enumerable: true,
11611 configurable: true,
11612 },
11613 );
11614 let inner = machine
11615 .allocate(HeapEntry::Object {
11616 properties: inner_properties,
11617 prototype: Some(machine.intrinsics.object_prototype),
11618 boxed_primitive: None,
11619 extensible: true,
11620 })
11621 .unwrap();
11622 let mut outer_properties = PropertyMap::default();
11623 outer_properties.insert(
11624 PropertyKey::Named(EcmaString::from_utf8("nested")),
11625 Property::Data {
11626 value: inner,
11627 writable: true,
11628 enumerable: true,
11629 configurable: true,
11630 },
11631 );
11632 let outer = machine
11633 .allocate(HeapEntry::Object {
11634 properties: outer_properties,
11635 prototype: Some(machine.intrinsics.object_prototype),
11636 boxed_primitive: None,
11637 extensible: true,
11638 })
11639 .unwrap();
11640 let symbol = machine
11641 .allocate(HeapEntry::Symbol {
11642 description: EcmaString::from_utf8("token"),
11643 })
11644 .unwrap();
11645 for value in [
11646 string,
11647 Value::int32(42),
11648 array,
11649 outer,
11650 Value::UNDEFINED,
11651 Value::NULL,
11652 symbol,
11653 ] {
11654 machine.call_value(log, console, &[value]).unwrap();
11655 }
11656 }
11657 assert_eq!(
11658 host.stdout,
11659 b"hello\n42\n[ 1, 'x' ]\n{ nested: { answer: 42 } }\nundefined\nnull\nSymbol(token)\n"
11660 );
11661 assert!(host.stderr.is_empty());
11662 }
11663
11664 #[test]
11665 fn console_and_process_properties_are_reassignable_and_env_is_live() {
11666 let module = verified(
11667 Vec::new(),
11668 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11669 );
11670 let mut host = CapabilityHost::default();
11671 {
11672 let mut machine = Machine::new(&module, &mut host, Limits::default());
11673 let console = machine.intrinsics.global("console").unwrap();
11674 let warn = machine.get_named_property(console, "warn").unwrap();
11675 machine
11676 .set_data_property(console, "warn", Value::int32(91))
11677 .unwrap();
11678 assert_eq!(
11679 machine.get_named_property(console, "warn").unwrap(),
11680 Value::int32(91)
11681 );
11682 machine.set_data_property(console, "warn", warn).unwrap();
11683
11684 let process = machine.intrinsics.global("process").unwrap();
11685 let env = machine.get_named_property(process, "env").unwrap();
11686 machine
11687 .set_data_property(env, "BAMTS_MODE", Value::int32(7))
11688 .unwrap();
11689 let value = machine.get_named_property(env, "BAMTS_MODE").unwrap();
11690 assert!(
11691 machine
11692 .string_text(value)
11693 .is_some_and(|text| text.eq_ascii("7"))
11694 );
11695 assert!(
11696 machine
11697 .delete_property(
11698 env,
11699 &PropertyKey::Named(EcmaString::from_utf8("BAMTS_MODE"))
11700 )
11701 .unwrap()
11702 );
11703 assert_eq!(
11704 machine.get_named_property(env, "BAMTS_MODE").unwrap(),
11705 Value::UNDEFINED
11706 );
11707 }
11708 assert_eq!(host.env("BAMTS_MODE"), None);
11709 }
11710
11711 #[test]
11712 fn independent_modules_keep_same_name_globals_isolated() {
11713 let dependency = |name: &str, value: i32| {
11714 program_module(
11715 name,
11716 vec![
11717 Constant::String(EcmaString::from_utf8("x")),
11718 Constant::Int32(value),
11719 ],
11720 vec![function(
11721 0,
11722 1,
11723 vec![
11724 Instruction::LoadConst {
11725 dst: reg(0),
11726 constant: cid(2),
11727 },
11728 Instruction::StoreGlobal {
11729 name: cid(1),
11730 value: reg(0),
11731 },
11732 Instruction::Return { value: reg(0) },
11733 ],
11734 Vec::new(),
11735 )],
11736 Vec::new(),
11737 vec![Binding {
11738 name: cid(1),
11739 kind: BindingKind::Hoisted,
11740 }],
11741 vec![Export {
11742 name: cid(1),
11743 source: ExportSource::Local(BindingId::new(0)),
11744 }],
11745 )
11746 };
11747 let root = program_module(
11748 "root",
11749 vec![
11750 Constant::String(EcmaString::from_utf8("left")),
11751 Constant::String(EcmaString::from_utf8("right")),
11752 Constant::String(EcmaString::from_utf8("x")),
11753 ],
11754 vec![function(
11755 0,
11756 5,
11757 vec![
11758 Instruction::LoadGlobal {
11759 dst: reg(0),
11760 name: cid(1),
11761 },
11762 Instruction::LoadGlobal {
11763 dst: reg(1),
11764 name: cid(2),
11765 },
11766 Instruction::LoadConst {
11767 dst: reg(2),
11768 constant: cid(3),
11769 },
11770 Instruction::GetProperty {
11771 dst: reg(3),
11772 object: reg(0),
11773 key: reg(2),
11774 },
11775 Instruction::GetProperty {
11776 dst: reg(4),
11777 object: reg(1),
11778 key: reg(2),
11779 },
11780 Instruction::Binary {
11781 dst: reg(0),
11782 op: BinaryOp::Add,
11783 left: reg(3),
11784 right: reg(4),
11785 },
11786 Instruction::Return { value: reg(0) },
11787 ],
11788 Vec::new(),
11789 )],
11790 vec![
11791 Edge {
11792 specifier: cid(1),
11793 target: EdgeTarget::Local(ModuleId::new(0)),
11794 kind: EdgeKind::Static,
11795 },
11796 Edge {
11797 specifier: cid(2),
11798 target: EdgeTarget::Local(ModuleId::new(1)),
11799 kind: EdgeKind::Static,
11800 },
11801 ],
11802 vec![
11803 Binding {
11804 name: cid(1),
11805 kind: BindingKind::Namespace {
11806 edge: EdgeId::new(0),
11807 },
11808 },
11809 Binding {
11810 name: cid(2),
11811 kind: BindingKind::Namespace {
11812 edge: EdgeId::new(1),
11813 },
11814 },
11815 ],
11816 Vec::new(),
11817 );
11818 let program = linked(vec![dependency("left", 1), dependency("right", 2), root], 2);
11819 assert_eq!(run_ok(&program).value, Value::int32(3));
11820 }
11821
11822 #[test]
11823 fn imported_binding_observes_post_link_mutation_live() {
11824 let dependency = program_module(
11825 "dependency",
11826 vec![
11827 Constant::String(EcmaString::from_utf8("x")),
11828 Constant::Int32(1),
11829 Constant::Int32(2),
11830 Constant::String(EcmaString::from_utf8("set")),
11831 ],
11832 vec![
11833 function(
11834 0,
11835 3,
11836 vec![
11837 Instruction::LoadConst {
11838 dst: reg(0),
11839 constant: cid(2),
11840 },
11841 Instruction::StoreGlobal {
11842 name: cid(1),
11843 value: reg(0),
11844 },
11845 Instruction::CreateArray { dst: reg(1) },
11846 Instruction::CreateClosure {
11847 dst: reg(2),
11848 function: FunctionId::new(1),
11849 captures: reg(1),
11850 },
11851 Instruction::StoreGlobal {
11852 name: cid(4),
11853 value: reg(2),
11854 },
11855 Instruction::Return { value: reg(0) },
11856 ],
11857 Vec::new(),
11858 ),
11859 function(
11860 0,
11861 1,
11862 vec![
11863 Instruction::LoadConst {
11864 dst: reg(0),
11865 constant: cid(3),
11866 },
11867 Instruction::StoreGlobal {
11868 name: cid(1),
11869 value: reg(0),
11870 },
11871 Instruction::Return { value: reg(0) },
11872 ],
11873 Vec::new(),
11874 ),
11875 ],
11876 Vec::new(),
11877 vec![
11878 Binding {
11879 name: cid(1),
11880 kind: BindingKind::Hoisted,
11881 },
11882 Binding {
11883 name: cid(4),
11884 kind: BindingKind::Hoisted,
11885 },
11886 ],
11887 vec![
11888 Export {
11889 name: cid(1),
11890 source: ExportSource::Local(BindingId::new(0)),
11891 },
11892 Export {
11893 name: cid(4),
11894 source: ExportSource::Local(BindingId::new(1)),
11895 },
11896 ],
11897 );
11898 let root = program_module(
11899 "root",
11900 vec![
11901 Constant::String(EcmaString::from_utf8("x")),
11902 Constant::String(EcmaString::from_utf8("set")),
11903 Constant::String(EcmaString::from_utf8("dep")),
11904 ],
11905 vec![function(
11906 0,
11907 3,
11908 vec![
11909 Instruction::LoadGlobal {
11910 dst: reg(0),
11911 name: cid(2),
11912 },
11913 Instruction::CreateArray { dst: reg(1) },
11914 Instruction::Call {
11915 dst: reg(2),
11916 callee: reg(0),
11917 this_value: reg(1),
11918 arguments: reg(1),
11919 },
11920 Instruction::LoadGlobal {
11921 dst: reg(0),
11922 name: cid(1),
11923 },
11924 Instruction::Return { value: reg(0) },
11925 ],
11926 Vec::new(),
11927 )],
11928 vec![Edge {
11929 specifier: cid(3),
11930 target: EdgeTarget::Local(ModuleId::new(0)),
11931 kind: EdgeKind::Static,
11932 }],
11933 vec![
11934 Binding {
11935 name: cid(1),
11936 kind: BindingKind::Imported {
11937 edge: EdgeId::new(0),
11938 name: cid(1),
11939 },
11940 },
11941 Binding {
11942 name: cid(2),
11943 kind: BindingKind::Imported {
11944 edge: EdgeId::new(0),
11945 name: cid(2),
11946 },
11947 },
11948 ],
11949 Vec::new(),
11950 );
11951 assert_eq!(
11952 run_ok(&linked(vec![dependency, root], 1)).value,
11953 Value::int32(2)
11954 );
11955 }
11956
11957 #[test]
11958 fn closure_globals_resolve_in_the_defining_module() {
11959 let dependency = program_module(
11960 "dependency",
11961 vec![
11962 Constant::String(EcmaString::from_utf8("x")),
11963 Constant::Int32(10),
11964 Constant::String(EcmaString::from_utf8("read")),
11965 ],
11966 vec![
11967 function(
11968 0,
11969 3,
11970 vec![
11971 Instruction::LoadConst {
11972 dst: reg(0),
11973 constant: cid(2),
11974 },
11975 Instruction::StoreGlobal {
11976 name: cid(1),
11977 value: reg(0),
11978 },
11979 Instruction::CreateArray { dst: reg(1) },
11980 Instruction::CreateClosure {
11981 dst: reg(2),
11982 function: FunctionId::new(1),
11983 captures: reg(1),
11984 },
11985 Instruction::StoreGlobal {
11986 name: cid(3),
11987 value: reg(2),
11988 },
11989 Instruction::Return { value: reg(0) },
11990 ],
11991 Vec::new(),
11992 ),
11993 function(
11994 0,
11995 1,
11996 vec![
11997 Instruction::LoadGlobal {
11998 dst: reg(0),
11999 name: cid(1),
12000 },
12001 Instruction::Return { value: reg(0) },
12002 ],
12003 Vec::new(),
12004 ),
12005 ],
12006 Vec::new(),
12007 vec![
12008 Binding {
12009 name: cid(1),
12010 kind: BindingKind::Hoisted,
12011 },
12012 Binding {
12013 name: cid(3),
12014 kind: BindingKind::Hoisted,
12015 },
12016 ],
12017 vec![Export {
12018 name: cid(3),
12019 source: ExportSource::Local(BindingId::new(1)),
12020 }],
12021 );
12022 let root = program_module(
12023 "root",
12024 vec![
12025 Constant::String(EcmaString::from_utf8("x")),
12026 Constant::Int32(20),
12027 Constant::String(EcmaString::from_utf8("read")),
12028 Constant::String(EcmaString::from_utf8("dep")),
12029 ],
12030 vec![function(
12031 0,
12032 4,
12033 vec![
12034 Instruction::LoadConst {
12035 dst: reg(0),
12036 constant: cid(2),
12037 },
12038 Instruction::StoreGlobal {
12039 name: cid(1),
12040 value: reg(0),
12041 },
12042 Instruction::LoadGlobal {
12043 dst: reg(1),
12044 name: cid(3),
12045 },
12046 Instruction::CreateArray { dst: reg(2) },
12047 Instruction::Call {
12048 dst: reg(3),
12049 callee: reg(1),
12050 this_value: reg(2),
12051 arguments: reg(2),
12052 },
12053 Instruction::Return { value: reg(3) },
12054 ],
12055 Vec::new(),
12056 )],
12057 vec![Edge {
12058 specifier: cid(4),
12059 target: EdgeTarget::Local(ModuleId::new(0)),
12060 kind: EdgeKind::Static,
12061 }],
12062 vec![
12063 Binding {
12064 name: cid(1),
12065 kind: BindingKind::Hoisted,
12066 },
12067 Binding {
12068 name: cid(3),
12069 kind: BindingKind::Imported {
12070 edge: EdgeId::new(0),
12071 name: cid(3),
12072 },
12073 },
12074 ],
12075 Vec::new(),
12076 );
12077 assert_eq!(
12078 run_ok(&linked(vec![dependency, root], 1)).value,
12079 Value::int32(10)
12080 );
12081 }
12082
12083 #[test]
12084 fn cycle_traps_a_lexical_read_before_initialization() {
12085 let first = program_module(
12086 "first",
12087 vec![
12088 Constant::String(EcmaString::from_utf8("a")),
12089 Constant::Int32(1),
12090 Constant::String(EcmaString::from_utf8("second")),
12091 ],
12092 vec![function(
12093 0,
12094 1,
12095 vec![
12096 Instruction::LoadConst {
12097 dst: reg(0),
12098 constant: cid(2),
12099 },
12100 Instruction::StoreGlobal {
12101 name: cid(1),
12102 value: reg(0),
12103 },
12104 Instruction::Return { value: reg(0) },
12105 ],
12106 Vec::new(),
12107 )],
12108 vec![Edge {
12109 specifier: cid(3),
12110 target: EdgeTarget::Local(ModuleId::new(1)),
12111 kind: EdgeKind::Static,
12112 }],
12113 vec![Binding {
12114 name: cid(1),
12115 kind: BindingKind::Lexical,
12116 }],
12117 vec![Export {
12118 name: cid(1),
12119 source: ExportSource::Local(BindingId::new(0)),
12120 }],
12121 );
12122 let second = program_module(
12123 "second",
12124 vec![
12125 Constant::String(EcmaString::from_utf8("a")),
12126 Constant::String(EcmaString::from_utf8("first")),
12127 ],
12128 vec![function(
12129 0,
12130 1,
12131 vec![
12132 Instruction::LoadGlobal {
12133 dst: reg(0),
12134 name: cid(1),
12135 },
12136 Instruction::Return { value: reg(0) },
12137 ],
12138 Vec::new(),
12139 )],
12140 vec![Edge {
12141 specifier: cid(2),
12142 target: EdgeTarget::Local(ModuleId::new(0)),
12143 kind: EdgeKind::Static,
12144 }],
12145 vec![Binding {
12146 name: cid(1),
12147 kind: BindingKind::Imported {
12148 edge: EdgeId::new(0),
12149 name: cid(1),
12150 },
12151 }],
12152 Vec::new(),
12153 );
12154 let program = linked(vec![first, second], 0);
12155 let mut host = TestHost;
12156 let error = Machine::new(&program, &mut host, Limits::default())
12157 .run()
12158 .unwrap_err();
12159 assert!(matches!(
12160 error.kind,
12161 RuntimeErrorKind::TemporalDeadZone { module, binding }
12162 if module == ModuleId::new(1) && binding == BindingId::new(0)
12163 ));
12164 }
12165
12166 #[test]
12167 fn cycle_reentry_with_a_hoisted_binding_completes() {
12168 let first = program_module(
12169 "first",
12170 vec![
12171 Constant::String(EcmaString::from_utf8("a")),
12172 Constant::Int32(1),
12173 Constant::String(EcmaString::from_utf8("second")),
12174 ],
12175 vec![function(
12176 0,
12177 1,
12178 vec![
12179 Instruction::LoadConst {
12180 dst: reg(0),
12181 constant: cid(2),
12182 },
12183 Instruction::StoreGlobal {
12184 name: cid(1),
12185 value: reg(0),
12186 },
12187 Instruction::Return { value: reg(0) },
12188 ],
12189 Vec::new(),
12190 )],
12191 vec![Edge {
12192 specifier: cid(3),
12193 target: EdgeTarget::Local(ModuleId::new(1)),
12194 kind: EdgeKind::Static,
12195 }],
12196 vec![Binding {
12197 name: cid(1),
12198 kind: BindingKind::Hoisted,
12199 }],
12200 vec![Export {
12201 name: cid(1),
12202 source: ExportSource::Local(BindingId::new(0)),
12203 }],
12204 );
12205 let second = program_module(
12206 "second",
12207 vec![
12208 Constant::String(EcmaString::from_utf8("a")),
12209 Constant::String(EcmaString::from_utf8("first")),
12210 ],
12211 vec![function(
12212 0,
12213 1,
12214 vec![
12215 Instruction::LoadGlobal {
12216 dst: reg(0),
12217 name: cid(1),
12218 },
12219 Instruction::Return { value: reg(0) },
12220 ],
12221 Vec::new(),
12222 )],
12223 vec![Edge {
12224 specifier: cid(2),
12225 target: EdgeTarget::Local(ModuleId::new(0)),
12226 kind: EdgeKind::Static,
12227 }],
12228 vec![Binding {
12229 name: cid(1),
12230 kind: BindingKind::Imported {
12231 edge: EdgeId::new(0),
12232 name: cid(1),
12233 },
12234 }],
12235 Vec::new(),
12236 );
12237 assert_eq!(
12238 run_ok(&linked(vec![first, second], 0)).value,
12239 Value::int32(1)
12240 );
12241 }
12242
12243 #[test]
12244 fn namespace_identity_reads_live_cells_and_enumerates_sorted_keys() {
12245 let dependency = program_module(
12246 "dependency",
12247 vec![
12248 Constant::String(EcmaString::from_utf8("z")),
12249 Constant::String(EcmaString::from_utf8("a")),
12250 Constant::String(EcmaString::from_utf8("mutate")),
12251 Constant::Int32(1),
12252 Constant::Int32(2),
12253 Constant::Int32(3),
12254 ],
12255 vec![
12256 function(
12257 0,
12258 4,
12259 vec![
12260 Instruction::LoadConst {
12261 dst: reg(0),
12262 constant: cid(4),
12263 },
12264 Instruction::StoreGlobal {
12265 name: cid(1),
12266 value: reg(0),
12267 },
12268 Instruction::LoadConst {
12269 dst: reg(0),
12270 constant: cid(5),
12271 },
12272 Instruction::StoreGlobal {
12273 name: cid(2),
12274 value: reg(0),
12275 },
12276 Instruction::CreateArray { dst: reg(1) },
12277 Instruction::CreateClosure {
12278 dst: reg(2),
12279 function: FunctionId::new(1),
12280 captures: reg(1),
12281 },
12282 Instruction::StoreGlobal {
12283 name: cid(3),
12284 value: reg(2),
12285 },
12286 Instruction::Return { value: reg(0) },
12287 ],
12288 Vec::new(),
12289 ),
12290 function(
12291 0,
12292 1,
12293 vec![
12294 Instruction::LoadConst {
12295 dst: reg(0),
12296 constant: cid(6),
12297 },
12298 Instruction::StoreGlobal {
12299 name: cid(1),
12300 value: reg(0),
12301 },
12302 Instruction::Return { value: reg(0) },
12303 ],
12304 Vec::new(),
12305 ),
12306 ],
12307 Vec::new(),
12308 vec![
12309 Binding {
12310 name: cid(1),
12311 kind: BindingKind::Hoisted,
12312 },
12313 Binding {
12314 name: cid(2),
12315 kind: BindingKind::Hoisted,
12316 },
12317 Binding {
12318 name: cid(3),
12319 kind: BindingKind::Hoisted,
12320 },
12321 ],
12322 vec![
12323 Export {
12324 name: cid(1),
12325 source: ExportSource::Local(BindingId::new(0)),
12326 },
12327 Export {
12328 name: cid(2),
12329 source: ExportSource::Local(BindingId::new(1)),
12330 },
12331 Export {
12332 name: cid(3),
12333 source: ExportSource::Local(BindingId::new(2)),
12334 },
12335 ],
12336 );
12337 let root = program_module(
12338 "root",
12339 vec![
12340 Constant::String(EcmaString::from_utf8("ns1")),
12341 Constant::String(EcmaString::from_utf8("ns2")),
12342 Constant::String(EcmaString::from_utf8("mutate")),
12343 Constant::String(EcmaString::from_utf8("z")),
12344 Constant::String(EcmaString::from_utf8("a")),
12345 Constant::String(EcmaString::from_utf8("dep")),
12346 Constant::String(EcmaString::from_utf8("Object")),
12347 Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
12348 Constant::String(EcmaString::from_utf8("value")),
12349 Constant::String(EcmaString::from_utf8("writable")),
12350 Constant::String(EcmaString::from_utf8("enumerable")),
12351 Constant::String(EcmaString::from_utf8("configurable")),
12352 Constant::String(EcmaString::from_utf8("missing")),
12353 ],
12354 vec![function(
12355 0,
12356 31,
12357 vec![
12358 Instruction::LoadGlobal {
12359 dst: reg(0),
12360 name: cid(1),
12361 },
12362 Instruction::LoadGlobal {
12363 dst: reg(1),
12364 name: cid(2),
12365 },
12366 Instruction::Binary {
12367 dst: reg(2),
12368 op: BinaryOp::StrictEqual,
12369 left: reg(0),
12370 right: reg(1),
12371 },
12372 Instruction::LoadGlobal {
12373 dst: reg(3),
12374 name: cid(3),
12375 },
12376 Instruction::CreateArray { dst: reg(4) },
12377 Instruction::Call {
12378 dst: reg(5),
12379 callee: reg(3),
12380 this_value: reg(4),
12381 arguments: reg(4),
12382 },
12383 Instruction::LoadConst {
12384 dst: reg(6),
12385 constant: cid(4),
12386 },
12387 Instruction::GetProperty {
12388 dst: reg(7),
12389 object: reg(0),
12390 key: reg(6),
12391 },
12392 Instruction::GetIterator {
12393 dst: reg(8),
12394 src: reg(0),
12395 kind: IteratorKind::Keys,
12396 },
12397 Instruction::IteratorNext {
12398 done: reg(9),
12399 value: reg(10),
12400 iterator: reg(8),
12401 },
12402 Instruction::LoadConst {
12403 dst: reg(11),
12404 constant: cid(5),
12405 },
12406 Instruction::Binary {
12407 dst: reg(12),
12408 op: BinaryOp::StrictEqual,
12409 left: reg(10),
12410 right: reg(11),
12411 },
12412 Instruction::IteratorNext {
12413 done: reg(9),
12414 value: reg(10),
12415 iterator: reg(8),
12416 },
12417 Instruction::LoadConst {
12418 dst: reg(13),
12419 constant: cid(3),
12420 },
12421 Instruction::Binary {
12422 dst: reg(5),
12423 op: BinaryOp::StrictEqual,
12424 left: reg(10),
12425 right: reg(13),
12426 },
12427 Instruction::IteratorNext {
12428 done: reg(9),
12429 value: reg(10),
12430 iterator: reg(8),
12431 },
12432 Instruction::Binary {
12433 dst: reg(14),
12434 op: BinaryOp::StrictEqual,
12435 left: reg(10),
12436 right: reg(6),
12437 },
12438 Instruction::LoadGlobal {
12439 dst: reg(15),
12440 name: cid(7),
12441 },
12442 Instruction::LoadConst {
12443 dst: reg(16),
12444 constant: cid(8),
12445 },
12446 Instruction::GetProperty {
12447 dst: reg(17),
12448 object: reg(15),
12449 key: reg(16),
12450 },
12451 Instruction::CreateArray { dst: reg(18) },
12452 Instruction::ArrayPush {
12453 array: reg(18),
12454 value: reg(0),
12455 },
12456 Instruction::ArrayPush {
12457 array: reg(18),
12458 value: reg(6),
12459 },
12460 Instruction::Call {
12461 dst: reg(19),
12462 callee: reg(17),
12463 this_value: reg(18),
12464 arguments: reg(18),
12465 },
12466 Instruction::LoadConst {
12467 dst: reg(20),
12468 constant: cid(9),
12469 },
12470 Instruction::GetProperty {
12471 dst: reg(21),
12472 object: reg(19),
12473 key: reg(20),
12474 },
12475 Instruction::LoadConst {
12476 dst: reg(22),
12477 constant: cid(10),
12478 },
12479 Instruction::GetProperty {
12480 dst: reg(23),
12481 object: reg(19),
12482 key: reg(22),
12483 },
12484 Instruction::LoadConst {
12485 dst: reg(24),
12486 constant: cid(11),
12487 },
12488 Instruction::GetProperty {
12489 dst: reg(25),
12490 object: reg(19),
12491 key: reg(24),
12492 },
12493 Instruction::LoadConst {
12494 dst: reg(26),
12495 constant: cid(12),
12496 },
12497 Instruction::GetProperty {
12498 dst: reg(27),
12499 object: reg(19),
12500 key: reg(26),
12501 },
12502 Instruction::CreateArray { dst: reg(28) },
12503 Instruction::LoadConst {
12504 dst: reg(29),
12505 constant: cid(13),
12506 },
12507 Instruction::ArrayPush {
12508 array: reg(28),
12509 value: reg(0),
12510 },
12511 Instruction::ArrayPush {
12512 array: reg(28),
12513 value: reg(29),
12514 },
12515 Instruction::Call {
12516 dst: reg(30),
12517 callee: reg(17),
12518 this_value: reg(28),
12519 arguments: reg(28),
12520 },
12521 Instruction::Return { value: reg(21) },
12522 ],
12523 Vec::new(),
12524 )],
12525 vec![Edge {
12526 specifier: cid(6),
12527 target: EdgeTarget::Local(ModuleId::new(0)),
12528 kind: EdgeKind::Static,
12529 }],
12530 vec![
12531 Binding {
12532 name: cid(1),
12533 kind: BindingKind::Namespace {
12534 edge: EdgeId::new(0),
12535 },
12536 },
12537 Binding {
12538 name: cid(2),
12539 kind: BindingKind::Namespace {
12540 edge: EdgeId::new(0),
12541 },
12542 },
12543 Binding {
12544 name: cid(3),
12545 kind: BindingKind::Imported {
12546 edge: EdgeId::new(0),
12547 name: cid(3),
12548 },
12549 },
12550 ],
12551 Vec::new(),
12552 );
12553 let execution = run_ok(&linked(vec![dependency, root], 1));
12554 assert_eq!(execution.value, Value::int32(3));
12555 assert_eq!(execution.entry_registers[2], Value::TRUE);
12556 assert_eq!(execution.entry_registers[5], Value::TRUE);
12557 assert_eq!(execution.entry_registers[12], Value::TRUE);
12558 assert_eq!(execution.entry_registers[14], Value::TRUE);
12559 assert_eq!(execution.entry_registers[23], Value::TRUE);
12560 assert_eq!(execution.entry_registers[25], Value::TRUE);
12561 assert_eq!(execution.entry_registers[27], Value::FALSE);
12562 assert_eq!(execution.entry_registers[30], Value::UNDEFINED);
12563 }
12564
12565 #[test]
12566 fn side_effect_module_runs_once_with_single_or_duplicate_static_edges() {
12567 for duplicate in [false, true] {
12568 let dependency = program_module(
12569 "dependency",
12570 vec![
12571 Constant::String(EcmaString::from_utf8("count")),
12572 Constant::Int32(0),
12573 Constant::Int32(1),
12574 ],
12575 vec![function(
12576 0,
12577 2,
12578 vec![
12579 Instruction::LoadGlobal {
12580 dst: reg(0),
12581 name: cid(1),
12582 },
12583 Instruction::JumpIfFalse {
12584 condition: reg(0),
12585 target: pc(3),
12586 },
12587 Instruction::Jump { target: pc(5) },
12588 Instruction::LoadConst {
12589 dst: reg(0),
12590 constant: cid(2),
12591 },
12592 Instruction::StoreGlobal {
12593 name: cid(1),
12594 value: reg(0),
12595 },
12596 Instruction::LoadConst {
12597 dst: reg(1),
12598 constant: cid(3),
12599 },
12600 Instruction::Binary {
12601 dst: reg(0),
12602 op: BinaryOp::Add,
12603 left: reg(0),
12604 right: reg(1),
12605 },
12606 Instruction::StoreGlobal {
12607 name: cid(1),
12608 value: reg(0),
12609 },
12610 Instruction::Return { value: reg(0) },
12611 ],
12612 Vec::new(),
12613 )],
12614 Vec::new(),
12615 vec![Binding {
12616 name: cid(1),
12617 kind: BindingKind::Hoisted,
12618 }],
12619 vec![Export {
12620 name: cid(1),
12621 source: ExportSource::Local(BindingId::new(0)),
12622 }],
12623 );
12624 let mut edges = vec![Edge {
12625 specifier: cid(2),
12626 target: EdgeTarget::Local(ModuleId::new(0)),
12627 kind: EdgeKind::Static,
12628 }];
12629 if duplicate {
12630 edges.push(Edge {
12631 specifier: cid(3),
12632 target: EdgeTarget::Local(ModuleId::new(0)),
12633 kind: EdgeKind::Static,
12634 });
12635 }
12636 let root = program_module(
12637 "root",
12638 vec![
12639 Constant::String(EcmaString::from_utf8("count")),
12640 Constant::String(EcmaString::from_utf8("dep-one")),
12641 Constant::String(EcmaString::from_utf8("dep-two")),
12642 ],
12643 vec![function(
12644 0,
12645 1,
12646 vec![
12647 Instruction::LoadGlobal {
12648 dst: reg(0),
12649 name: cid(1),
12650 },
12651 Instruction::Return { value: reg(0) },
12652 ],
12653 Vec::new(),
12654 )],
12655 edges,
12656 vec![Binding {
12657 name: cid(1),
12658 kind: BindingKind::Imported {
12659 edge: EdgeId::new(0),
12660 name: cid(1),
12661 },
12662 }],
12663 Vec::new(),
12664 );
12665 assert_eq!(
12666 run_ok(&linked(vec![dependency, root], 1)).value,
12667 Value::int32(1)
12668 );
12669 }
12670 }
12671
12672 #[test]
12673 fn failed_module_rethrows_the_identical_stored_value() {
12674 let module = program_module(
12675 "throws",
12676 Vec::new(),
12677 vec![function(
12678 0,
12679 1,
12680 vec![
12681 Instruction::CreateObject { dst: reg(0) },
12682 Instruction::Throw { value: reg(0) },
12683 ],
12684 Vec::new(),
12685 )],
12686 Vec::new(),
12687 Vec::new(),
12688 Vec::new(),
12689 );
12690 let program = linked(vec![module], 0);
12691 let mut host = TestHost;
12692 let mut machine = Machine::new(&program, &mut host, Limits::default());
12693 machine.frames.clear();
12694 machine.live_registers = 0;
12695 machine.instantiate_modules().unwrap();
12696 let first = machine.evaluate_module(ModuleId::new(0)).unwrap_err();
12697 let second = machine.evaluate_module(ModuleId::new(0)).unwrap_err();
12698 let RuntimeErrorKind::UncaughtThrow { value: first, .. } = first.kind else {
12699 panic!("module must fail by throwing");
12700 };
12701 let RuntimeErrorKind::UncaughtThrow { value: second, .. } = second.kind else {
12702 panic!("stored failure must remain a throw");
12703 };
12704 assert_eq!(first, second);
12705 assert!(first.as_heap_ref().is_some());
12706 }
12707
12708 #[test]
12709 fn external_static_edge_is_a_typed_runtime_error() {
12710 let module = program_module(
12711 "root",
12712 vec![Constant::String(EcmaString::from_utf8("external"))],
12713 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
12714 vec![Edge {
12715 specifier: cid(1),
12716 target: EdgeTarget::External,
12717 kind: EdgeKind::Static,
12718 }],
12719 Vec::new(),
12720 Vec::new(),
12721 );
12722 let program = linked(vec![module], 0);
12723 let mut host = TestHost;
12724 let error = Machine::new(&program, &mut host, Limits::default())
12725 .run()
12726 .unwrap_err();
12727 assert!(matches!(
12728 error.kind,
12729 RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
12730 if module == ModuleId::new(0) && edge == EdgeId::new(0)
12731 ));
12732 }
12733
12734 #[test]
12735 fn external_module_and_export_names_preserve_unicode() {
12736 for (specifier, export) in [("módulo", "value"), ("external", "café")] {
12737 let module = program_module(
12738 "root",
12739 vec![
12740 Constant::String(EcmaString::from_utf8(export)),
12741 Constant::String(EcmaString::from_utf8(specifier)),
12742 ],
12743 vec![function(
12744 0,
12745 1,
12746 vec![
12747 Instruction::LoadGlobal {
12748 dst: reg(0),
12749 name: cid(1),
12750 },
12751 Instruction::Return { value: reg(0) },
12752 ],
12753 Vec::new(),
12754 )],
12755 vec![Edge {
12756 specifier: cid(2),
12757 target: EdgeTarget::External,
12758 kind: EdgeKind::Static,
12759 }],
12760 vec![Binding {
12761 name: cid(1),
12762 kind: BindingKind::Imported {
12763 edge: EdgeId::new(0),
12764 name: cid(1),
12765 },
12766 }],
12767 Vec::new(),
12768 );
12769 let program = linked(vec![module], 0);
12770 let mut host = TestHost;
12771 let mut machine = Machine::new(&program, &mut host, Limits::default());
12772 machine.registry.external.insert(
12773 EcmaString::from_utf8(specifier),
12774 ExternalModuleInstance {
12775 namespace: Value::UNDEFINED,
12776 exports: BTreeMap::from([(
12777 EcmaString::from_utf8(export),
12778 ExternalExport {
12779 value: Value::int32(7),
12780 cell: None,
12781 },
12782 )]),
12783 internals: BTreeMap::new(),
12784 },
12785 );
12786
12787 assert_eq!(machine.run().unwrap().value, Value::int32(7));
12788 }
12789 }
12790
12791 #[test]
12792 fn dynamic_import_preserves_cycles_identity_and_single_evaluation() {
12793 let root = program_module(
12794 "root",
12795 vec![
12796 Constant::String(EcmaString::from_utf8("./dependency")),
12797 Constant::String(EcmaString::from_utf8("count")),
12798 Constant::Int32(0),
12799 Constant::String(EcmaString::from_utf8("value")),
12800 ],
12801 vec![function(
12802 0,
12803 7,
12804 vec![
12805 Instruction::LoadConst {
12806 dst: reg(0),
12807 constant: cid(3),
12808 },
12809 Instruction::StoreGlobal {
12810 name: cid(2),
12811 value: reg(0),
12812 },
12813 Instruction::Import {
12814 dst: reg(1),
12815 specifier: cid(1),
12816 },
12817 Instruction::Import {
12818 dst: reg(2),
12819 specifier: cid(1),
12820 },
12821 Instruction::Binary {
12822 dst: reg(3),
12823 op: BinaryOp::StrictEqual,
12824 left: reg(1),
12825 right: reg(2),
12826 },
12827 Instruction::LoadConst {
12828 dst: reg(4),
12829 constant: cid(4),
12830 },
12831 Instruction::GetProperty {
12832 dst: reg(5),
12833 object: reg(2),
12834 key: reg(4),
12835 },
12836 Instruction::LoadGlobal {
12837 dst: reg(6),
12838 name: cid(2),
12839 },
12840 Instruction::Return { value: reg(5) },
12841 ],
12842 Vec::new(),
12843 )],
12844 vec![Edge {
12845 specifier: cid(1),
12846 target: EdgeTarget::Local(ModuleId::new(1)),
12847 kind: EdgeKind::Dynamic,
12848 }],
12849 Vec::new(),
12850 Vec::new(),
12851 );
12852 let dependency = program_module(
12853 "dependency",
12854 vec![
12855 Constant::String(EcmaString::from_utf8("./root")),
12856 Constant::String(EcmaString::from_utf8("count")),
12857 Constant::Int32(1),
12858 Constant::Int32(7),
12859 Constant::String(EcmaString::from_utf8("value")),
12860 ],
12861 vec![function(
12862 0,
12863 3,
12864 vec![
12865 Instruction::LoadGlobal {
12866 dst: reg(0),
12867 name: cid(2),
12868 },
12869 Instruction::LoadConst {
12870 dst: reg(1),
12871 constant: cid(3),
12872 },
12873 Instruction::Binary {
12874 dst: reg(2),
12875 op: BinaryOp::Add,
12876 left: reg(0),
12877 right: reg(1),
12878 },
12879 Instruction::StoreGlobal {
12880 name: cid(2),
12881 value: reg(2),
12882 },
12883 Instruction::LoadConst {
12884 dst: reg(0),
12885 constant: cid(4),
12886 },
12887 Instruction::StoreGlobal {
12888 name: cid(5),
12889 value: reg(0),
12890 },
12891 Instruction::Return { value: reg(0) },
12892 ],
12893 Vec::new(),
12894 )],
12895 vec![Edge {
12896 specifier: cid(1),
12897 target: EdgeTarget::Local(ModuleId::new(0)),
12898 kind: EdgeKind::Static,
12899 }],
12900 vec![Binding {
12901 name: cid(5),
12902 kind: BindingKind::Hoisted,
12903 }],
12904 vec![Export {
12905 name: cid(5),
12906 source: ExportSource::Local(BindingId::new(0)),
12907 }],
12908 );
12909
12910 let execution = run_ok(&linked(vec![root, dependency], 0));
12911 assert_eq!(execution.value, Value::int32(7));
12912 assert_eq!(execution.entry_registers[1], execution.entry_registers[2]);
12913 assert_eq!(execution.entry_registers[3], Value::TRUE);
12914 assert_eq!(execution.entry_registers[6], Value::int32(1));
12915 }
12916
12917 #[test]
12918 fn dynamic_import_counts_live_registers_and_retries_engine_failures() {
12919 let target = program_module(
12920 "target",
12921 Vec::new(),
12922 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
12923 Vec::new(),
12924 Vec::new(),
12925 Vec::new(),
12926 );
12927 let root = program_module(
12928 "root",
12929 vec![Constant::String(EcmaString::from_utf8("./target"))],
12930 vec![function(
12931 0,
12932 1,
12933 vec![
12934 Instruction::Import {
12935 dst: reg(0),
12936 specifier: cid(1),
12937 },
12938 Instruction::Return { value: reg(0) },
12939 ],
12940 Vec::new(),
12941 )],
12942 vec![Edge {
12943 specifier: cid(1),
12944 target: EdgeTarget::Local(ModuleId::new(1)),
12945 kind: EdgeKind::Dynamic,
12946 }],
12947 Vec::new(),
12948 Vec::new(),
12949 );
12950 let program = linked(vec![root, target], 0);
12951 let mut host = TestHost;
12952 let mut machine = Machine::new(
12953 &program,
12954 &mut host,
12955 Limits {
12956 max_total_registers: 1,
12957 ..Limits::default()
12958 },
12959 );
12960 machine.frames.clear();
12961 machine.live_registers = 0;
12962 machine.instantiate_modules().unwrap();
12963
12964 let error = machine.evaluate_import(ModuleId::new(0)).unwrap_err();
12965 assert!(matches!(
12966 error.kind,
12967 RuntimeErrorKind::RegisterLimitExceeded { limit: 1 }
12968 ));
12969 assert_eq!(machine.frames.len(), 0);
12970 assert_eq!(machine.live_registers, 0);
12971
12972 machine.limits.max_total_registers = 2;
12973 machine.evaluate_import(ModuleId::new(0)).unwrap();
12974 }
12975
12976 #[test]
12977 fn dynamic_import_rethrows_one_stored_failure_at_each_import_site() {
12978 let root = program_module(
12979 "root",
12980 vec![
12981 Constant::String(EcmaString::from_utf8("./target")),
12982 Constant::String(EcmaString::from_utf8("count")),
12983 Constant::Int32(0),
12984 ],
12985 vec![function(
12986 0,
12987 4,
12988 vec![
12989 Instruction::LoadConst {
12990 dst: reg(0),
12991 constant: cid(3),
12992 },
12993 Instruction::StoreGlobal {
12994 name: cid(2),
12995 value: reg(0),
12996 },
12997 Instruction::Import {
12998 dst: reg(0),
12999 specifier: cid(1),
13000 },
13001 Instruction::Halt,
13002 Instruction::Import {
13003 dst: reg(0),
13004 specifier: cid(1),
13005 },
13006 Instruction::Halt,
13007 Instruction::LoadGlobal {
13008 dst: reg(3),
13009 name: cid(2),
13010 },
13011 Instruction::Return { value: reg(2) },
13012 ],
13013 vec![
13014 ExceptionHandler {
13015 start: pc(2),
13016 end: pc(3),
13017 handler: pc(4),
13018 catch_register: reg(1),
13019 },
13020 ExceptionHandler {
13021 start: pc(4),
13022 end: pc(5),
13023 handler: pc(6),
13024 catch_register: reg(2),
13025 },
13026 ],
13027 )],
13028 vec![Edge {
13029 specifier: cid(1),
13030 target: EdgeTarget::Local(ModuleId::new(1)),
13031 kind: EdgeKind::Dynamic,
13032 }],
13033 Vec::new(),
13034 Vec::new(),
13035 );
13036 let target = program_module(
13037 "target",
13038 vec![
13039 Constant::String(EcmaString::from_utf8("count")),
13040 Constant::Int32(1),
13041 Constant::Int32(9),
13042 ],
13043 vec![function(
13044 0,
13045 3,
13046 vec![
13047 Instruction::LoadGlobal {
13048 dst: reg(0),
13049 name: cid(1),
13050 },
13051 Instruction::LoadConst {
13052 dst: reg(1),
13053 constant: cid(2),
13054 },
13055 Instruction::Binary {
13056 dst: reg(2),
13057 op: BinaryOp::Add,
13058 left: reg(0),
13059 right: reg(1),
13060 },
13061 Instruction::StoreGlobal {
13062 name: cid(1),
13063 value: reg(2),
13064 },
13065 Instruction::LoadConst {
13066 dst: reg(0),
13067 constant: cid(3),
13068 },
13069 Instruction::Throw { value: reg(0) },
13070 ],
13071 Vec::new(),
13072 )],
13073 Vec::new(),
13074 Vec::new(),
13075 Vec::new(),
13076 );
13077
13078 let execution = run_ok(&linked(vec![root, target], 0));
13079 assert_eq!(execution.value, Value::int32(9));
13080 assert_eq!(execution.entry_registers[1], Value::int32(9));
13081 assert_eq!(execution.entry_registers[2], Value::int32(9));
13082 assert_eq!(execution.entry_registers[3], Value::int32(1));
13083 }
13084
13085 #[test]
13086 fn dynamic_import_returns_the_registered_external_namespace() {
13087 let module = program_module(
13088 "root",
13089 vec![Constant::String(EcmaString::from_utf8("external"))],
13090 vec![function(
13091 0,
13092 3,
13093 vec![
13094 Instruction::Import {
13095 dst: reg(0),
13096 specifier: cid(1),
13097 },
13098 Instruction::Import {
13099 dst: reg(1),
13100 specifier: cid(1),
13101 },
13102 Instruction::Binary {
13103 dst: reg(2),
13104 op: BinaryOp::StrictEqual,
13105 left: reg(0),
13106 right: reg(1),
13107 },
13108 Instruction::Return { value: reg(2) },
13109 ],
13110 Vec::new(),
13111 )],
13112 vec![Edge {
13113 specifier: cid(1),
13114 target: EdgeTarget::External,
13115 kind: EdgeKind::Dynamic,
13116 }],
13117 Vec::new(),
13118 Vec::new(),
13119 );
13120 let program = linked(vec![module], 0);
13121 let mut host = TestHost;
13122 let mut machine = Machine::new(&program, &mut host, Limits::default());
13123 let namespace = machine
13124 .allocate(HeapEntry::Object {
13125 properties: PropertyMap::default(),
13126 prototype: Some(machine.intrinsics.object_prototype),
13127 boxed_primitive: None,
13128 extensible: true,
13129 })
13130 .unwrap();
13131 machine.registry.external.insert(
13132 EcmaString::from_utf8("external"),
13133 ExternalModuleInstance {
13134 namespace,
13135 exports: BTreeMap::new(),
13136 internals: BTreeMap::new(),
13137 },
13138 );
13139
13140 let execution = machine.run().unwrap();
13141 assert_eq!(execution.value, Value::TRUE);
13142 assert_eq!(execution.entry_registers[0], namespace);
13143 assert_eq!(execution.entry_registers[1], namespace);
13144 }
13145
13146 #[test]
13147 fn dynamic_import_resolution_is_requester_scoped() {
13148 let requester = |name, target| {
13149 program_module(
13150 name,
13151 vec![Constant::String(EcmaString::from_utf8("./target"))],
13152 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13153 vec![Edge {
13154 specifier: cid(1),
13155 target: EdgeTarget::Local(ModuleId::new(target)),
13156 kind: EdgeKind::Dynamic,
13157 }],
13158 Vec::new(),
13159 Vec::new(),
13160 )
13161 };
13162 let target = |name| {
13163 program_module(
13164 name,
13165 Vec::new(),
13166 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13167 Vec::new(),
13168 Vec::new(),
13169 Vec::new(),
13170 )
13171 };
13172 let program = linked(
13173 vec![
13174 requester("first", 2),
13175 requester("second", 3),
13176 target("first-target"),
13177 target("second-target"),
13178 ],
13179 0,
13180 );
13181 let mut host = TestHost;
13182 let machine = Machine::new(&program, &mut host, Limits::default());
13183
13184 assert_eq!(
13185 machine.resolve_import(ModuleId::new(0), cid(1)),
13186 Ok(ImportTarget::Local(ModuleId::new(2)))
13187 );
13188 assert_eq!(
13189 machine.resolve_import(ModuleId::new(1), cid(1)),
13190 Ok(ImportTarget::Local(ModuleId::new(3)))
13191 );
13192 }
13193
13194 #[test]
13195 fn dynamic_import_of_a_missing_external_is_a_runtime_error() {
13196 let module = program_module(
13197 "root",
13198 vec![Constant::String(EcmaString::from_utf8("dynamic"))],
13199 vec![function(
13200 0,
13201 1,
13202 vec![
13203 Instruction::Import {
13204 dst: reg(0),
13205 specifier: cid(1),
13206 },
13207 Instruction::Return { value: reg(0) },
13208 ],
13209 Vec::new(),
13210 )],
13211 vec![Edge {
13212 specifier: cid(1),
13213 target: EdgeTarget::External,
13214 kind: EdgeKind::Dynamic,
13215 }],
13216 Vec::new(),
13217 Vec::new(),
13218 );
13219 let program = linked(vec![module], 0);
13220 let mut host = TestHost;
13221 let error = Machine::new(&program, &mut host, Limits::default())
13222 .run()
13223 .unwrap_err();
13224 assert!(matches!(
13225 error.kind,
13226 RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
13227 if module == ModuleId::new(0) && edge == EdgeId::new(0)
13228 ));
13229 }
13230
13231 #[test]
13232 fn unbound_global_names_fall_back_to_the_realm_global_map() {
13233 let program = verified(
13234 vec![
13235 Constant::String(EcmaString::from_utf8("realmOnly")),
13236 Constant::Int32(7),
13237 ],
13238 vec![function(
13239 0,
13240 1,
13241 vec![
13242 Instruction::LoadConst {
13243 dst: reg(0),
13244 constant: cid(1),
13245 },
13246 Instruction::StoreGlobal {
13247 name: cid(0),
13248 value: reg(0),
13249 },
13250 Instruction::LoadGlobal {
13251 dst: reg(0),
13252 name: cid(0),
13253 },
13254 Instruction::Return { value: reg(0) },
13255 ],
13256 Vec::new(),
13257 )],
13258 );
13259 assert_eq!(run_ok(&program).value, Value::int32(7));
13260 }
13261
13262 #[test]
13263 fn module_cell_limit_is_enforced_before_evaluation() {
13264 let module = program_module(
13265 "root",
13266 vec![Constant::String(EcmaString::from_utf8("x"))],
13267 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13268 Vec::new(),
13269 vec![Binding {
13270 name: cid(1),
13271 kind: BindingKind::Hoisted,
13272 }],
13273 Vec::new(),
13274 );
13275 let program = linked(vec![module], 0);
13276 let mut host = TestHost;
13277 let error = Machine::new(
13278 &program,
13279 &mut host,
13280 Limits {
13281 max_module_cells: 0,
13282 ..Limits::default()
13283 },
13284 )
13285 .run()
13286 .unwrap_err();
13287 assert!(matches!(
13288 error.kind,
13289 RuntimeErrorKind::ModuleCellLimitExceeded { limit: 0 }
13290 ));
13291 }
13292 #[test]
13293 fn imported_binding_store_throws_without_mutating_the_exporter() {
13294 let dependency = program_module(
13295 "dependency",
13296 vec![
13297 Constant::String(EcmaString::from_utf8("x")),
13298 Constant::Int32(1),
13299 ],
13300 vec![function(
13301 0,
13302 1,
13303 vec![
13304 Instruction::LoadConst {
13305 dst: reg(0),
13306 constant: cid(2),
13307 },
13308 Instruction::StoreGlobal {
13309 name: cid(1),
13310 value: reg(0),
13311 },
13312 Instruction::Return { value: reg(0) },
13313 ],
13314 Vec::new(),
13315 )],
13316 Vec::new(),
13317 vec![Binding {
13318 name: cid(1),
13319 kind: BindingKind::Hoisted,
13320 }],
13321 vec![Export {
13322 name: cid(1),
13323 source: ExportSource::Local(BindingId::new(0)),
13324 }],
13325 );
13326 let root = program_module(
13327 "root",
13328 vec![
13329 Constant::String(EcmaString::from_utf8("x")),
13330 Constant::Int32(2),
13331 Constant::String(EcmaString::from_utf8("dep")),
13332 ],
13333 vec![function(
13334 0,
13335 1,
13336 vec![
13337 Instruction::LoadConst {
13338 dst: reg(0),
13339 constant: cid(2),
13340 },
13341 Instruction::StoreGlobal {
13342 name: cid(1),
13343 value: reg(0),
13344 },
13345 Instruction::Return { value: reg(0) },
13346 ],
13347 Vec::new(),
13348 )],
13349 vec![Edge {
13350 specifier: cid(3),
13351 target: EdgeTarget::Local(ModuleId::new(0)),
13352 kind: EdgeKind::Static,
13353 }],
13354 vec![Binding {
13355 name: cid(1),
13356 kind: BindingKind::Imported {
13357 edge: EdgeId::new(0),
13358 name: cid(1),
13359 },
13360 }],
13361 Vec::new(),
13362 );
13363 let program = linked(vec![dependency, root], 1);
13364 let mut host = TestHost;
13365 let mut machine = Machine::new(&program, &mut host, Limits::default());
13366 machine.frames.clear();
13367 machine.live_registers = 0;
13368 machine.instantiate_modules().unwrap();
13369 assert!(machine.evaluate_module(ModuleId::new(1)).is_err());
13370 let exporter = machine.registry.modules[0].binding_cells[0].unwrap();
13371 assert_eq!(machine.registry.cells[exporter.0].value, Value::int32(1));
13372 }
13373
13374 #[test]
13375 fn namespace_descriptor_propagates_temporal_dead_zone() {
13376 let root = program_module(
13377 "root",
13378 vec![
13379 Constant::String(EcmaString::from_utf8("x")),
13380 Constant::Int32(1),
13381 Constant::String(EcmaString::from_utf8("dependency")),
13382 ],
13383 vec![function(
13384 0,
13385 1,
13386 vec![
13387 Instruction::LoadConst {
13388 dst: reg(0),
13389 constant: cid(2),
13390 },
13391 Instruction::StoreGlobal {
13392 name: cid(1),
13393 value: reg(0),
13394 },
13395 Instruction::Return { value: reg(0) },
13396 ],
13397 Vec::new(),
13398 )],
13399 vec![Edge {
13400 specifier: cid(3),
13401 target: EdgeTarget::Local(ModuleId::new(1)),
13402 kind: EdgeKind::Static,
13403 }],
13404 vec![Binding {
13405 name: cid(1),
13406 kind: BindingKind::Lexical,
13407 }],
13408 vec![Export {
13409 name: cid(1),
13410 source: ExportSource::Local(BindingId::new(0)),
13411 }],
13412 );
13413 let dependency = program_module(
13414 "dependency",
13415 vec![
13416 Constant::String(EcmaString::from_utf8("ns")),
13417 Constant::String(EcmaString::from_utf8("root")),
13418 Constant::String(EcmaString::from_utf8("Object")),
13419 Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
13420 Constant::String(EcmaString::from_utf8("x")),
13421 ],
13422 vec![namespace_descriptor_entry()],
13423 vec![Edge {
13424 specifier: cid(2),
13425 target: EdgeTarget::Local(ModuleId::new(0)),
13426 kind: EdgeKind::Static,
13427 }],
13428 vec![Binding {
13429 name: cid(1),
13430 kind: BindingKind::Namespace {
13431 edge: EdgeId::new(0),
13432 },
13433 }],
13434 Vec::new(),
13435 );
13436 let program = linked(vec![root, dependency], 0);
13437 let mut host = TestHost;
13438 let error = Machine::new(&program, &mut host, Limits::default())
13439 .run()
13440 .expect_err("descriptor reads uninitialized namespace export");
13441 assert!(matches!(
13442 error.kind,
13443 RuntimeErrorKind::TemporalDeadZone { module, binding }
13444 if module == ModuleId::new(0) && binding == BindingId::new(0)
13445 ));
13446 }
13447
13448 #[test]
13449 fn namespace_descriptor_propagates_external_linkage_error() {
13450 let exported = program_module(
13451 "exported",
13452 vec![
13453 Constant::String(EcmaString::from_utf8("x")),
13454 Constant::String(EcmaString::from_utf8("external")),
13455 ],
13456 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13457 vec![Edge {
13458 specifier: cid(2),
13459 target: EdgeTarget::External,
13460 kind: EdgeKind::Dynamic,
13461 }],
13462 Vec::new(),
13463 vec![Export {
13464 name: cid(1),
13465 source: ExportSource::Indirect {
13466 edge: EdgeId::new(0),
13467 name: cid(1),
13468 },
13469 }],
13470 );
13471 let importer = program_module(
13472 "importer",
13473 vec![
13474 Constant::String(EcmaString::from_utf8("ns")),
13475 Constant::String(EcmaString::from_utf8("exported")),
13476 Constant::String(EcmaString::from_utf8("Object")),
13477 Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
13478 Constant::String(EcmaString::from_utf8("x")),
13479 ],
13480 vec![namespace_descriptor_entry()],
13481 vec![Edge {
13482 specifier: cid(2),
13483 target: EdgeTarget::Local(ModuleId::new(0)),
13484 kind: EdgeKind::Static,
13485 }],
13486 vec![Binding {
13487 name: cid(1),
13488 kind: BindingKind::Namespace {
13489 edge: EdgeId::new(0),
13490 },
13491 }],
13492 Vec::new(),
13493 );
13494 let program = linked(vec![exported, importer], 1);
13495 let mut host = TestHost;
13496 let error = Machine::new(&program, &mut host, Limits::default())
13497 .run()
13498 .expect_err("descriptor resolves external namespace export");
13499 assert!(matches!(
13500 error.kind,
13501 RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
13502 if module == ModuleId::new(0) && edge == EdgeId::new(0)
13503 ));
13504 }
13505
13506 #[test]
13507 fn installed_script_uses_machine_wide_id_and_keeps_its_code() {
13508 let root = verified(
13509 Vec::new(),
13510 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13511 );
13512 let script = Arc::new(verified(
13513 vec![Constant::Int32(42)],
13514 vec![function(
13515 0,
13516 1,
13517 vec![
13518 Instruction::LoadConst {
13519 dst: reg(0),
13520 constant: cid(0),
13521 },
13522 Instruction::Return { value: reg(0) },
13523 ],
13524 Vec::new(),
13525 )],
13526 ));
13527 let mut host = TestHost;
13528 let mut machine = Machine::new(&root, &mut host, Limits::default());
13529 machine.instantiate_modules().unwrap();
13530 let module = machine.install_script_reserving(script, 0, 0).unwrap();
13531
13532 assert_eq!(module, ModuleId::new(root.modules().len() as u32));
13533 assert!(machine.program().module(module).is_none());
13534 assert_eq!(
13535 machine.module_code(module).constants()[0],
13536 Constant::Int32(42)
13537 );
13538
13539 let closure = machine
13540 .allocate(HeapEntry::Function {
13541 module,
13542 function: FunctionId::new(0),
13543 captures: Vec::new(),
13544 properties: PropertyMap::default(),
13545 prototype: Some(machine.intrinsics.function_prototype),
13546 extensible: true,
13547 })
13548 .unwrap();
13549 assert!(matches!(
13550 machine.call_value(closure, Value::UNDEFINED, &[]),
13551 Ok(value) if value == Value::int32(42)
13552 ));
13553 }
13554
13555 #[test]
13556 fn installed_script_rejects_non_classic_programs_and_enforces_limit() {
13557 let root = verified(
13558 Vec::new(),
13559 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13560 );
13561 let two_modules = Arc::new(linked(
13562 vec![
13563 program_module(
13564 "first",
13565 Vec::new(),
13566 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13567 Vec::new(),
13568 Vec::new(),
13569 Vec::new(),
13570 ),
13571 program_module(
13572 "second",
13573 Vec::new(),
13574 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13575 Vec::new(),
13576 Vec::new(),
13577 Vec::new(),
13578 ),
13579 ],
13580 0,
13581 ));
13582 let script = Arc::new(verified(
13583 Vec::new(),
13584 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13585 ));
13586 let mut host = TestHost;
13587 let mut machine = Machine::new(
13588 &root,
13589 &mut host,
13590 Limits {
13591 max_dynamic_modules: 1,
13592 ..Limits::default()
13593 },
13594 );
13595 machine.instantiate_modules().unwrap();
13596
13597 assert!(matches!(
13598 machine.install_script_reserving(two_modules, 0, 0),
13599 Err(RuntimeErrorKind::InvalidDynamicScript { .. })
13600 ));
13601 machine
13602 .install_script_reserving(script.clone(), 0, 0)
13603 .unwrap();
13604 assert!(matches!(
13605 machine.install_script_reserving(script, 0, 0),
13606 Err(RuntimeErrorKind::DynamicModuleLimitExceeded { limit: 1 })
13607 ));
13608 }
13609
13610 #[test]
13611 fn script_heap_cost_counts_scalar_constant_slots() {
13612 let entry = || vec![function(0, 1, vec![Instruction::Halt], Vec::new())];
13613 let empty = verified(Vec::new(), entry());
13614 let constants = vec![Constant::Int32(0); 128];
13615 let scalars = verified(constants.clone(), entry());
13616
13617 let added = Machine::<TestHost>::script_heap_cost(&scalars)
13618 - Machine::<TestHost>::script_heap_cost(&empty);
13619
13620 assert!(added >= constants.len() * std::mem::size_of::<Constant>());
13621 }
13622
13623 #[test]
13624 fn script_heap_cost_includes_verification_storage() {
13625 let small = verified(
13626 Vec::new(),
13627 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13628 );
13629 let large = verified(
13630 Vec::new(),
13631 vec![function(0, 130, vec![Instruction::Halt], Vec::new())],
13632 );
13633 let small_verification = small.modules()[0].code.verification_bytes();
13634 let large_verification = large.modules()[0].code.verification_bytes();
13635
13636 assert_eq!(
13637 Machine::<TestHost>::script_heap_cost(&large)
13638 - Machine::<TestHost>::script_heap_cost(&small),
13639 large_verification - small_verification
13640 );
13641 }
13642 #[test]
13643 fn promise_resolver_settles_once_and_reactions_wait_for_drain() {
13644 let program = verified(
13645 vec![
13646 Constant::String(EcmaString::from_utf8("resolve")),
13647 Constant::String(EcmaString::from_utf8("reject")),
13648 Constant::String(EcmaString::from_utf8("observed")),
13649 ],
13650 vec![
13651 function(0, 1, vec![Instruction::Halt], Vec::new()),
13652 function(
13653 2,
13654 2,
13655 vec![
13656 Instruction::StoreGlobal {
13657 name: cid(0),
13658 value: reg(0),
13659 },
13660 Instruction::StoreGlobal {
13661 name: cid(1),
13662 value: reg(1),
13663 },
13664 Instruction::Return { value: reg(0) },
13665 ],
13666 Vec::new(),
13667 ),
13668 function(
13669 1,
13670 1,
13671 vec![
13672 Instruction::StoreGlobal {
13673 name: cid(2),
13674 value: reg(0),
13675 },
13676 Instruction::Return { value: reg(0) },
13677 ],
13678 Vec::new(),
13679 ),
13680 ],
13681 );
13682 let mut host = TestHost;
13683 let mut machine = Machine::new(&program, &mut host, Limits::default());
13684 machine.frames.clear();
13685 machine.live_registers = 0;
13686 let executor = machine
13687 .allocate(HeapEntry::Function {
13688 module: ModuleId::new(0),
13689 function: FunctionId::new(1),
13690 captures: Vec::new(),
13691 properties: PropertyMap::default(),
13692 prototype: Some(machine.intrinsics.function_prototype),
13693 extensible: true,
13694 })
13695 .unwrap();
13696 let observer = machine
13697 .allocate(HeapEntry::Function {
13698 module: ModuleId::new(0),
13699 function: FunctionId::new(2),
13700 captures: Vec::new(),
13701 properties: PropertyMap::default(),
13702 prototype: Some(machine.intrinsics.function_prototype),
13703 extensible: true,
13704 })
13705 .unwrap();
13706 let constructor = machine.intrinsics.global("Promise").unwrap();
13707 let constructor_index = machine.runtime_slot(constructor).unwrap().unwrap();
13708 let HeapEntry::NativeFunction {
13709 callable: NativeCallable::Builtin(constructor_id),
13710 ..
13711 } = machine.heap[constructor_index]
13712 else {
13713 panic!("Promise must be a native constructor");
13714 };
13715 let BuiltinOutcome::Value(promise) = machine
13716 .call_builtin(constructor_id, Value::UNDEFINED, &[executor], true)
13717 .unwrap()
13718 else {
13719 panic!("Promise construction returns a Promise");
13720 };
13721 let then = machine.get_named_property(promise, "then").unwrap();
13722 machine
13723 .call_value(then, promise, &[observer])
13724 .expect("then returns a derived Promise");
13725 let resolve = machine
13726 .globals
13727 .get(&EcmaString::from_utf8("resolve"))
13728 .copied()
13729 .unwrap();
13730 let reject = machine
13731 .globals
13732 .get(&EcmaString::from_utf8("reject"))
13733 .copied()
13734 .unwrap();
13735 assert_eq!(
13736 machine
13737 .call_value(resolve, Value::UNDEFINED, &[Value::int32(1)])
13738 .unwrap(),
13739 Value::UNDEFINED
13740 );
13741 assert_eq!(
13742 machine
13743 .call_value(reject, Value::UNDEFINED, &[Value::int32(2)])
13744 .unwrap(),
13745 Value::UNDEFINED
13746 );
13747 assert!(
13748 !machine
13749 .globals
13750 .contains_key(&EcmaString::from_utf8("observed"))
13751 );
13752
13753 let drain = machine.drain_microtasks().unwrap();
13754 assert_eq!(drain.executed, 1);
13755 assert!(drain.uncaught.is_empty());
13756 assert_eq!(
13757 machine
13758 .globals
13759 .get(&EcmaString::from_utf8("observed"))
13760 .copied(),
13761 Some(Value::int32(1))
13762 );
13763 }
13764
13765 #[test]
13766 fn promise_resolution_adopts_thenables_with_a_fresh_resolver() {
13767 let program = verified(
13768 vec![
13769 Constant::String(EcmaString::from_utf8("resolve")),
13770 Constant::String(EcmaString::from_utf8("reject")),
13771 Constant::String(EcmaString::from_utf8("observed")),
13772 Constant::Int32(7),
13773 Constant::Int32(8),
13774 Constant::Int32(9),
13775 Constant::Undefined,
13776 ],
13777 vec![
13778 function(0, 1, vec![Instruction::Halt], Vec::new()),
13779 function(
13780 2,
13781 2,
13782 vec![
13783 Instruction::StoreGlobal {
13784 name: cid(0),
13785 value: reg(0),
13786 },
13787 Instruction::StoreGlobal {
13788 name: cid(1),
13789 value: reg(1),
13790 },
13791 Instruction::Return { value: reg(0) },
13792 ],
13793 Vec::new(),
13794 ),
13795 function(
13796 1,
13797 1,
13798 vec![
13799 Instruction::StoreGlobal {
13800 name: cid(2),
13801 value: reg(0),
13802 },
13803 Instruction::Return { value: reg(0) },
13804 ],
13805 Vec::new(),
13806 ),
13807 function(
13808 2,
13809 6,
13810 vec![
13811 Instruction::LoadConst {
13812 dst: reg(2),
13813 constant: cid(3),
13814 },
13815 Instruction::CreateArray { dst: reg(3) },
13816 Instruction::ArrayPush {
13817 array: reg(3),
13818 value: reg(2),
13819 },
13820 Instruction::LoadConst {
13821 dst: reg(4),
13822 constant: cid(6),
13823 },
13824 Instruction::Call {
13825 dst: reg(5),
13826 callee: reg(0),
13827 this_value: reg(4),
13828 arguments: reg(3),
13829 },
13830 Instruction::LoadConst {
13831 dst: reg(2),
13832 constant: cid(4),
13833 },
13834 Instruction::CreateArray { dst: reg(3) },
13835 Instruction::ArrayPush {
13836 array: reg(3),
13837 value: reg(2),
13838 },
13839 Instruction::Call {
13840 dst: reg(5),
13841 callee: reg(1),
13842 this_value: reg(4),
13843 arguments: reg(3),
13844 },
13845 Instruction::LoadConst {
13846 dst: reg(2),
13847 constant: cid(5),
13848 },
13849 Instruction::Throw { value: reg(2) },
13850 ],
13851 Vec::new(),
13852 ),
13853 ],
13854 );
13855 let mut host = TestHost;
13856 let mut machine = Machine::new(&program, &mut host, Limits::default());
13857 machine.frames.clear();
13858 machine.live_registers = 0;
13859 let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
13860 machine
13861 .allocate(HeapEntry::Function {
13862 module: ModuleId::new(0),
13863 function: FunctionId::new(function),
13864 captures: Vec::new(),
13865 properties: PropertyMap::default(),
13866 prototype: Some(machine.intrinsics.function_prototype),
13867 extensible: true,
13868 })
13869 .unwrap()
13870 };
13871 let executor = runtime_function(&mut machine, 1);
13872 let observer = runtime_function(&mut machine, 2);
13873 let then_callback = runtime_function(&mut machine, 3);
13874 let thenable = machine
13875 .allocate(HeapEntry::Object {
13876 properties: PropertyMap::default(),
13877 prototype: Some(machine.intrinsics.object_prototype),
13878 boxed_primitive: None,
13879 extensible: true,
13880 })
13881 .unwrap();
13882 machine
13883 .set_data_property(thenable, "then", then_callback)
13884 .unwrap();
13885
13886 let constructor = machine.intrinsics.global("Promise").unwrap();
13887 let constructor_index = machine.runtime_slot(constructor).unwrap().unwrap();
13888 let HeapEntry::NativeFunction {
13889 callable: NativeCallable::Builtin(constructor_id),
13890 ..
13891 } = machine.heap[constructor_index]
13892 else {
13893 panic!("Promise must be a native constructor");
13894 };
13895 let BuiltinOutcome::Value(promise) = machine
13896 .call_builtin(constructor_id, Value::UNDEFINED, &[executor], true)
13897 .unwrap()
13898 else {
13899 panic!("Promise construction returns a Promise");
13900 };
13901 let resolve = machine
13902 .globals
13903 .get(&EcmaString::from_utf8("resolve"))
13904 .copied()
13905 .unwrap();
13906 let reject = machine
13907 .globals
13908 .get(&EcmaString::from_utf8("reject"))
13909 .copied()
13910 .unwrap();
13911 machine
13912 .call_value(resolve, Value::UNDEFINED, &[thenable])
13913 .unwrap();
13914 let then = machine.get_named_property(promise, "then").unwrap();
13915 machine.call_value(then, promise, &[observer]).unwrap();
13916 machine
13917 .call_value(reject, Value::UNDEFINED, &[Value::int32(9)])
13918 .unwrap();
13919 assert!(
13920 !machine
13921 .globals
13922 .contains_key(&EcmaString::from_utf8("observed"))
13923 );
13924
13925 let drain = machine.drain_microtasks().unwrap();
13926 assert_eq!(drain.executed, 2);
13927 assert!(drain.uncaught.is_empty());
13928 assert_eq!(
13929 machine
13930 .globals
13931 .get(&EcmaString::from_utf8("observed"))
13932 .copied(),
13933 Some(Value::int32(7))
13934 );
13935 }
13936
13937 #[test]
13938 fn queue_microtask_drains_fifo_including_jobs_added_during_drain() {
13939 let program = verified(
13940 vec![
13941 Constant::String(EcmaString::from_utf8("order")),
13942 Constant::String(EcmaString::from_utf8("queueMicrotask")),
13943 Constant::String(EcmaString::from_utf8("third")),
13944 Constant::Int32(1),
13945 Constant::Int32(2),
13946 Constant::Int32(3),
13947 Constant::Undefined,
13948 ],
13949 vec![
13950 function(0, 1, vec![Instruction::Halt], Vec::new()),
13951 function(
13952 0,
13953 7,
13954 vec![
13955 Instruction::LoadGlobal {
13956 dst: reg(0),
13957 name: cid(0),
13958 },
13959 Instruction::LoadConst {
13960 dst: reg(1),
13961 constant: cid(3),
13962 },
13963 Instruction::ArrayPush {
13964 array: reg(0),
13965 value: reg(1),
13966 },
13967 Instruction::LoadGlobal {
13968 dst: reg(2),
13969 name: cid(1),
13970 },
13971 Instruction::LoadGlobal {
13972 dst: reg(3),
13973 name: cid(2),
13974 },
13975 Instruction::CreateArray { dst: reg(4) },
13976 Instruction::ArrayPush {
13977 array: reg(4),
13978 value: reg(3),
13979 },
13980 Instruction::LoadConst {
13981 dst: reg(5),
13982 constant: cid(6),
13983 },
13984 Instruction::Call {
13985 dst: reg(6),
13986 callee: reg(2),
13987 this_value: reg(5),
13988 arguments: reg(4),
13989 },
13990 Instruction::Return { value: reg(1) },
13991 ],
13992 Vec::new(),
13993 ),
13994 function(
13995 0,
13996 2,
13997 vec![
13998 Instruction::LoadGlobal {
13999 dst: reg(0),
14000 name: cid(0),
14001 },
14002 Instruction::LoadConst {
14003 dst: reg(1),
14004 constant: cid(4),
14005 },
14006 Instruction::ArrayPush {
14007 array: reg(0),
14008 value: reg(1),
14009 },
14010 Instruction::Return { value: reg(1) },
14011 ],
14012 Vec::new(),
14013 ),
14014 function(
14015 0,
14016 2,
14017 vec![
14018 Instruction::LoadGlobal {
14019 dst: reg(0),
14020 name: cid(0),
14021 },
14022 Instruction::LoadConst {
14023 dst: reg(1),
14024 constant: cid(5),
14025 },
14026 Instruction::ArrayPush {
14027 array: reg(0),
14028 value: reg(1),
14029 },
14030 Instruction::Return { value: reg(1) },
14031 ],
14032 Vec::new(),
14033 ),
14034 ],
14035 );
14036 let mut host = TestHost;
14037 let mut machine = Machine::new(&program, &mut host, Limits::default());
14038 machine.frames.clear();
14039 machine.live_registers = 0;
14040 let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
14041 machine
14042 .allocate(HeapEntry::Function {
14043 module: ModuleId::new(0),
14044 function: FunctionId::new(function),
14045 captures: Vec::new(),
14046 properties: PropertyMap::default(),
14047 prototype: Some(machine.intrinsics.function_prototype),
14048 extensible: true,
14049 })
14050 .unwrap()
14051 };
14052 let first = runtime_function(&mut machine, 1);
14053 let second = runtime_function(&mut machine, 2);
14054 let third = runtime_function(&mut machine, 3);
14055 let order = machine
14056 .allocate(HeapEntry::Array {
14057 elements: Vec::new(),
14058 properties: PropertyMap::default(),
14059 prototype: Some(machine.intrinsics.array_prototype),
14060 extensible: true,
14061 length_writable: true,
14062 })
14063 .unwrap();
14064 machine
14065 .globals
14066 .insert(EcmaString::from_utf8("order"), order);
14067 machine
14068 .globals
14069 .insert(EcmaString::from_utf8("third"), third);
14070 let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14071 machine
14072 .call_value(queue, Value::UNDEFINED, &[first])
14073 .unwrap();
14074 machine
14075 .call_value(queue, Value::UNDEFINED, &[second])
14076 .unwrap();
14077
14078 let drain = machine.drain_microtasks().unwrap();
14079 assert_eq!(drain.executed, 3);
14080 assert!(drain.uncaught.is_empty());
14081 let index = machine.runtime_slot(order).unwrap().unwrap();
14082 let HeapEntry::Array { elements, .. } = &machine.heap[index] else {
14083 panic!("order remains an array");
14084 };
14085 assert_eq!(
14086 elements,
14087 &[Value::int32(1), Value::int32(2), Value::int32(3)]
14088 );
14089 }
14090
14091 #[test]
14092 fn queue_microtask_reports_callback_throws_and_continues() {
14093 let program = verified(
14094 vec![
14095 Constant::Int32(7),
14096 Constant::Int32(1),
14097 Constant::String(EcmaString::from_utf8("observed")),
14098 ],
14099 vec![
14100 function(0, 1, vec![Instruction::Halt], Vec::new()),
14101 function(
14102 0,
14103 1,
14104 vec![
14105 Instruction::LoadConst {
14106 dst: reg(0),
14107 constant: cid(0),
14108 },
14109 Instruction::Throw { value: reg(0) },
14110 ],
14111 Vec::new(),
14112 ),
14113 function(
14114 0,
14115 1,
14116 vec![
14117 Instruction::LoadConst {
14118 dst: reg(0),
14119 constant: cid(1),
14120 },
14121 Instruction::StoreGlobal {
14122 name: cid(2),
14123 value: reg(0),
14124 },
14125 Instruction::Return { value: reg(0) },
14126 ],
14127 Vec::new(),
14128 ),
14129 ],
14130 );
14131 let mut host = TestHost;
14132 let mut machine = Machine::new(&program, &mut host, Limits::default());
14133 machine.frames.clear();
14134 machine.live_registers = 0;
14135 let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
14136 machine
14137 .allocate(HeapEntry::Function {
14138 module: ModuleId::new(0),
14139 function: FunctionId::new(function),
14140 captures: Vec::new(),
14141 properties: PropertyMap::default(),
14142 prototype: Some(machine.intrinsics.function_prototype),
14143 extensible: true,
14144 })
14145 .unwrap()
14146 };
14147 let throwing = runtime_function(&mut machine, 1);
14148 let observer = runtime_function(&mut machine, 2);
14149 let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14150 machine
14151 .call_value(queue, Value::UNDEFINED, &[throwing])
14152 .unwrap();
14153 machine
14154 .call_value(queue, Value::UNDEFINED, &[observer])
14155 .unwrap();
14156
14157 let drain = machine.drain_microtasks().unwrap();
14158 assert_eq!(drain.executed, 2);
14159 assert_eq!(
14160 drain.uncaught,
14161 vec![CallbackException {
14162 value: Value::int32(7),
14163 origin: ThrowOrigin::Bytecode,
14164 }]
14165 );
14166 assert_eq!(
14167 machine
14168 .globals
14169 .get(&EcmaString::from_utf8("observed"))
14170 .copied(),
14171 Some(Value::int32(1))
14172 );
14173 }
14174
14175 #[test]
14176 fn microtask_boundaries_preserve_the_queued_head() {
14177 let program = verified(
14178 vec![Constant::Undefined],
14179 vec![
14180 function(0, 1, vec![Instruction::Halt], Vec::new()),
14181 function(
14182 0,
14183 1,
14184 vec![
14185 Instruction::LoadConst {
14186 dst: reg(0),
14187 constant: cid(0),
14188 },
14189 Instruction::Return { value: reg(0) },
14190 ],
14191 Vec::new(),
14192 ),
14193 ],
14194 );
14195 let mut host = TestHost;
14196 let mut machine = Machine::new(
14197 &program,
14198 &mut host,
14199 Limits {
14200 max_microtasks: 1,
14201 ..Limits::default()
14202 },
14203 );
14204 machine.frames.clear();
14205 machine.live_registers = 0;
14206 let callback = machine
14207 .allocate(HeapEntry::Function {
14208 module: ModuleId::new(0),
14209 function: FunctionId::new(1),
14210 captures: Vec::new(),
14211 properties: PropertyMap::default(),
14212 prototype: Some(machine.intrinsics.function_prototype),
14213 extensible: true,
14214 })
14215 .unwrap();
14216 let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14217 assert!(matches!(
14218 machine.call_value(queue, Value::UNDEFINED, &[Value::int32(1)]),
14219 Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
14220 ));
14221 machine
14222 .call_value(queue, Value::UNDEFINED, &[callback])
14223 .unwrap();
14224 assert!(matches!(
14225 machine.call_value(queue, Value::UNDEFINED, &[callback]),
14226 Err(EvalFailure::Runtime(
14227 RuntimeErrorKind::MicrotaskQueueLimitExceeded { limit: 1 }
14228 ))
14229 ));
14230
14231 let fuel = machine.fuel;
14232 machine.microtask_drain_active = true;
14233 let reentry = machine.drain_microtasks().unwrap_err();
14234 assert!(matches!(
14235 reentry.kind,
14236 RuntimeErrorKind::MicrotaskDrainReentry
14237 ));
14238 assert_eq!(machine.fuel, fuel);
14239 assert_eq!(machine.microtasks.len(), 1);
14240 machine.microtask_drain_active = false;
14241
14242 machine.fuel = 0;
14243 let exhausted = machine.drain_microtasks().unwrap_err();
14244 assert!(matches!(
14245 exhausted.kind,
14246 RuntimeErrorKind::FuelExhausted { .. }
14247 ));
14248 assert!(!machine.microtask_drain_active);
14249 assert_eq!(machine.microtasks.len(), 1);
14250
14251 machine.fuel = 100;
14252 let drain = machine.drain_microtasks().unwrap();
14253 assert_eq!(drain.executed, 1);
14254 assert!(machine.microtasks.is_empty());
14255 }
14256
14257 #[derive(Default)]
14260 struct ManualTimerState {
14261 live: std::collections::BTreeMap<u64, u64>,
14262 reports: std::collections::VecDeque<TimerWakeup>,
14263 scheduled: Vec<(u64, u32)>,
14264 cancelled: Vec<u64>,
14265 fail_schedule: bool,
14266 fail_poll: bool,
14267 }
14268
14269 #[derive(Clone, Default)]
14270 struct ManualTimerProvider {
14271 state: std::rc::Rc<std::cell::RefCell<ManualTimerState>>,
14272 }
14273
14274 impl TimerProvider for ManualTimerProvider {
14275 fn schedule(&mut self, id: u64, delay_ms: u32) -> Result<u64, TimerError> {
14276 let mut state = self.state.borrow_mut();
14277 state.scheduled.push((id, delay_ms));
14278 if state.fail_schedule {
14279 return Err(TimerError::new("manual schedule failure"));
14280 }
14281 let deadline = u64::from(delay_ms);
14282 state.live.insert(id, deadline);
14283 Ok(deadline)
14284 }
14285
14286 fn cancel(&mut self, id: u64) -> Result<bool, TimerError> {
14287 let mut state = self.state.borrow_mut();
14288 state.cancelled.push(id);
14289 Ok(state.live.remove(&id).is_some())
14290 }
14291
14292 fn poll_expired(&mut self, output: &mut Vec<TimerWakeup>) -> Result<(), TimerError> {
14293 let mut state = self.state.borrow_mut();
14294 if state.fail_poll {
14295 return Err(TimerError::new("manual poll failure"));
14296 }
14297 output.extend(state.reports.drain(..));
14298 Ok(())
14299 }
14300
14301 fn wait_expired(&mut self) -> Result<Option<TimerWakeup>, TimerError> {
14302 Ok(self.state.borrow_mut().reports.pop_front())
14303 }
14304
14305 fn has_pending(&self) -> bool {
14306 !self.state.borrow().live.is_empty()
14307 }
14308 }
14309
14310 #[derive(Default)]
14311 struct TimerTestHost {
14312 provider: ManualTimerProvider,
14313 }
14314
14315 impl Host for TimerTestHost {
14316 fn timers(&mut self) -> Option<&mut (dyn TimerProvider + 'static)> {
14317 Some(&mut self.provider)
14318 }
14319 }
14320
14321 fn timer_program() -> Program<Verified> {
14322 verified(
14323 vec![
14324 Constant::String(EcmaString::from_utf8("a")),
14325 Constant::String(EcmaString::from_utf8("b")),
14326 Constant::String(EcmaString::from_utf8("this_seen")),
14327 Constant::String(EcmaString::from_utf8("arg_seen")),
14328 Constant::Int32(1),
14329 Constant::Int32(7),
14330 ],
14331 vec![
14332 function(0, 1, vec![Instruction::Halt], Vec::new()),
14333 function(
14334 0,
14335 1,
14336 vec![
14337 Instruction::LoadConst {
14338 dst: reg(0),
14339 constant: cid(4),
14340 },
14341 Instruction::StoreGlobal {
14342 name: cid(0),
14343 value: reg(0),
14344 },
14345 Instruction::Return { value: reg(0) },
14346 ],
14347 Vec::new(),
14348 ),
14349 function(
14350 0,
14351 1,
14352 vec![
14353 Instruction::LoadConst {
14354 dst: reg(0),
14355 constant: cid(4),
14356 },
14357 Instruction::StoreGlobal {
14358 name: cid(1),
14359 value: reg(0),
14360 },
14361 Instruction::Return { value: reg(0) },
14362 ],
14363 Vec::new(),
14364 ),
14365 function(
14366 1,
14367 2,
14368 vec![
14369 Instruction::LoadThis { dst: reg(1) },
14370 Instruction::StoreGlobal {
14371 name: cid(2),
14372 value: reg(1),
14373 },
14374 Instruction::StoreGlobal {
14375 name: cid(3),
14376 value: reg(0),
14377 },
14378 Instruction::Return { value: reg(0) },
14379 ],
14380 Vec::new(),
14381 ),
14382 function(
14383 0,
14384 1,
14385 vec![
14386 Instruction::LoadConst {
14387 dst: reg(0),
14388 constant: cid(5),
14389 },
14390 Instruction::Throw { value: reg(0) },
14391 ],
14392 Vec::new(),
14393 ),
14394 ],
14395 )
14396 }
14397
14398 fn timer_fn(machine: &mut Machine<'_, TimerTestHost>, index: u32) -> Value {
14399 machine
14400 .allocate(HeapEntry::Function {
14401 module: ModuleId::new(0),
14402 function: FunctionId::new(index),
14403 captures: Vec::new(),
14404 properties: PropertyMap::default(),
14405 prototype: Some(machine.intrinsics.function_prototype),
14406 extensible: true,
14407 })
14408 .unwrap()
14409 }
14410
14411 fn read_global(machine: &Machine<'_, TimerTestHost>, name: &str) -> Option<Value> {
14412 machine.globals.get(&EcmaString::from_utf8(name)).copied()
14413 }
14414
14415 fn set_timeout_global(machine: &Machine<'_, TimerTestHost>) -> Value {
14416 machine
14417 .intrinsics
14418 .global("setTimeout")
14419 .expect("setTimeout is installed")
14420 }
14421
14422 fn schedule_nested_timer(
14423 machine: &mut Machine<'_, TimerTestHost>,
14424 _this: Value,
14425 _args: &[Value],
14426 _constructing: bool,
14427 ) -> Result<BuiltinOutcome, EvalFailure> {
14428 let callback = machine
14429 .globals
14430 .get(&EcmaString::from_utf8("nestedCallback"))
14431 .copied()
14432 .expect("test installs nested callback");
14433 let set_timeout = set_timeout_global(machine);
14434 machine.call_value(set_timeout, Value::UNDEFINED, &[callback, Value::int32(1)])?;
14435 Ok(BuiltinOutcome::Value(Value::UNDEFINED))
14436 }
14437
14438 fn timer_native(
14439 machine: &mut Machine<'_, TimerTestHost>,
14440 name: &'static str,
14441 handler: crate::intrinsics::BuiltinHandler<TimerTestHost>,
14442 ) -> Value {
14443 let id = machine
14444 .intrinsics
14445 .builtins
14446 .register(crate::intrinsics::BuiltinDef {
14447 name,
14448 length: 0,
14449 handler,
14450 });
14451 crate::intrinsics::native_function(&mut machine.heap, id, name, 0)
14452 }
14453
14454 #[test]
14455 fn timers_are_absent_without_the_capability() {
14456 let program = timer_program();
14457 let mut host = TestHost;
14458 let mut machine = Machine::new(&program, &mut host, Limits::default());
14459 machine.frames.clear();
14460 machine.live_registers = 0;
14461 assert!(machine.intrinsics.global("setTimeout").is_none());
14462 assert!(machine.intrinsics.global("clearTimeout").is_none());
14463 assert!(!machine.has_pending_timers());
14464 assert_eq!(
14465 machine.run_one_expired_timer().unwrap(),
14466 TimerRun::default()
14467 );
14468 assert!(!machine.wait_for_timer_expiry().unwrap());
14469 }
14470
14471 #[test]
14472 fn set_timeout_rejects_a_non_callable_callback_before_coercion() {
14473 let program = timer_program();
14474 let mut host = TimerTestHost::default();
14475 let shared = host.provider.state.clone();
14476 let mut machine = Machine::new(&program, &mut host, Limits::default());
14477 machine.frames.clear();
14478 machine.live_registers = 0;
14479 let set_timeout = set_timeout_global(&machine);
14480 let failure = machine
14481 .call_value(
14482 set_timeout,
14483 Value::UNDEFINED,
14484 &[Value::int32(3), Value::int32(5)],
14485 )
14486 .unwrap_err();
14487 assert!(matches!(
14488 failure,
14489 EvalFailure::Throw(ThrowOrigin::TypeError { .. })
14490 ));
14491 assert!(shared.borrow().scheduled.is_empty());
14493 assert!(!machine.has_pending_timers());
14494 }
14495
14496 #[test]
14497 fn set_timeout_clamps_and_truncates_like_node() {
14498 let program = timer_program();
14499 let mut host = TimerTestHost::default();
14500 let shared = host.provider.state.clone();
14501 let mut machine = Machine::new(&program, &mut host, Limits::default());
14502 machine.frames.clear();
14503 machine.live_registers = 0;
14504 let set_timeout = set_timeout_global(&machine);
14505 let callback = timer_fn(&mut machine, 1);
14506 for delay in [
14507 Value::int32(0),
14508 Value::number(-5.0),
14509 Value::number(f64::NAN),
14510 Value::number(2_147_483_648.0),
14511 Value::int32(2_147_483_647),
14512 Value::number(3.9),
14513 ] {
14514 machine
14515 .call_value(set_timeout, Value::UNDEFINED, &[callback, delay])
14516 .unwrap();
14517 }
14518 let delays: Vec<u32> = shared.borrow().scheduled.iter().map(|(_, d)| *d).collect();
14519 assert_eq!(delays, vec![1, 1, 1, 1, 2_147_483_647, 3]);
14520 let ids: Vec<u64> = shared
14522 .borrow()
14523 .scheduled
14524 .iter()
14525 .map(|(id, _)| *id)
14526 .collect();
14527 assert_eq!(ids, vec![1, 2, 3, 4, 5, 6]);
14528 }
14529
14530 #[test]
14531 fn same_deadline_timers_run_in_registration_order_despite_reverse_reports() {
14532 let program = timer_program();
14533 let mut host = TimerTestHost::default();
14534 let shared = host.provider.state.clone();
14535 let mut machine = Machine::new(&program, &mut host, Limits::default());
14536 machine.frames.clear();
14537 machine.live_registers = 0;
14538 let set_timeout = set_timeout_global(&machine);
14539 let a = timer_fn(&mut machine, 1);
14540 let b = timer_fn(&mut machine, 2);
14541 machine
14542 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(5)])
14543 .unwrap();
14544 machine
14545 .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(5)])
14546 .unwrap();
14547 shared.borrow_mut().reports.push_back(TimerWakeup {
14549 id: 2,
14550 deadline_ms: 5,
14551 });
14552 let first = machine.run_one_expired_timer().unwrap();
14553 assert_eq!(first.executed, 1);
14554 assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14555 assert_eq!(read_global(&machine, "b"), None);
14556 let second = machine.run_one_expired_timer().unwrap();
14557 assert_eq!(second.executed, 1);
14558 assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14559 assert!(!machine.has_pending_timers());
14560 }
14561
14562 #[test]
14563 fn a_shorter_deadline_beats_an_older_sequence() {
14564 let program = timer_program();
14565 let mut host = TimerTestHost::default();
14566 let shared = host.provider.state.clone();
14567 let mut machine = Machine::new(&program, &mut host, Limits::default());
14568 machine.frames.clear();
14569 machine.live_registers = 0;
14570 let set_timeout = set_timeout_global(&machine);
14571 let a = timer_fn(&mut machine, 1);
14572 let b = timer_fn(&mut machine, 2);
14573 machine
14574 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(5)])
14575 .unwrap();
14576 machine
14577 .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(3)])
14578 .unwrap();
14579 shared.borrow_mut().reports.push_back(TimerWakeup {
14580 id: 1,
14581 deadline_ms: 5,
14582 });
14583 machine.run_one_expired_timer().unwrap();
14584 assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14585 assert_eq!(read_global(&machine, "a"), None);
14586 }
14587
14588 #[test]
14589 fn clear_timeout_prevents_a_ready_timer_and_ignores_stale_ids() {
14590 let program = timer_program();
14591 let mut host = TimerTestHost::default();
14592 let shared = host.provider.state.clone();
14593 let mut machine = Machine::new(&program, &mut host, Limits::default());
14594 machine.frames.clear();
14595 machine.live_registers = 0;
14596 let set_timeout = set_timeout_global(&machine);
14597 let clear_timeout = machine.intrinsics.global("clearTimeout").unwrap();
14598 let a = timer_fn(&mut machine, 1);
14599 let b = timer_fn(&mut machine, 2);
14600 let handle_a = machine
14601 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14602 .unwrap();
14603 machine
14604 .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(3)])
14605 .unwrap();
14606 shared.borrow_mut().reports.push_back(TimerWakeup {
14608 id: 1,
14609 deadline_ms: 3,
14610 });
14611 machine
14612 .call_value(clear_timeout, Value::UNDEFINED, &[handle_a])
14613 .unwrap();
14614 assert!(shared.borrow().cancelled.contains(&1));
14615 machine
14617 .call_value(clear_timeout, Value::UNDEFINED, &[Value::int32(1)])
14618 .unwrap();
14619 shared.borrow_mut().reports.push_back(TimerWakeup {
14620 id: 2,
14621 deadline_ms: 3,
14622 });
14623 let run = machine.run_one_expired_timer().unwrap();
14624 assert_eq!(run.executed, 1);
14625 assert_eq!(read_global(&machine, "a"), None);
14626 assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14627 }
14628
14629 #[test]
14630 fn clear_timeout_accepts_a_direct_positive_integer_id() {
14631 let program = timer_program();
14632 let mut host = TimerTestHost::default();
14633 let shared = host.provider.state.clone();
14634 let mut machine = Machine::new(&program, &mut host, Limits::default());
14635 machine.frames.clear();
14636 machine.live_registers = 0;
14637 let set_timeout = set_timeout_global(&machine);
14638 let clear_timeout = machine.intrinsics.global("clearTimeout").unwrap();
14639 let a = timer_fn(&mut machine, 1);
14640 machine
14641 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14642 .unwrap();
14643 machine
14644 .call_value(clear_timeout, Value::UNDEFINED, &[Value::int32(1)])
14645 .unwrap();
14646 assert!(!machine.has_pending_timers());
14647 shared.borrow_mut().reports.push_back(TimerWakeup {
14648 id: 1,
14649 deadline_ms: 3,
14650 });
14651 assert_eq!(machine.run_one_expired_timer().unwrap().executed, 0);
14652
14653 machine.next_timer_id = Some(u64::MAX);
14654 let handle = machine
14655 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14656 .unwrap();
14657 machine
14658 .call_value(
14659 clear_timeout,
14660 Value::UNDEFINED,
14661 &[Value::number(u64::MAX as f64)],
14662 )
14663 .unwrap();
14664 assert!(machine.has_pending_timers());
14665 machine
14666 .call_value(clear_timeout, Value::UNDEFINED, &[handle])
14667 .unwrap();
14668 assert!(!machine.has_pending_timers());
14669 machine
14671 .call_value(clear_timeout, Value::UNDEFINED, &[Value::UNDEFINED])
14672 .unwrap();
14673 }
14674
14675 #[test]
14676 fn timer_callback_receives_trailing_args_and_the_handle_as_this() {
14677 let program = timer_program();
14678 let mut host = TimerTestHost::default();
14679 let shared = host.provider.state.clone();
14680 let mut machine = Machine::new(&program, &mut host, Limits::default());
14681 machine.frames.clear();
14682 machine.live_registers = 0;
14683 let set_timeout = set_timeout_global(&machine);
14684 let callback = timer_fn(&mut machine, 3);
14685 let handle = machine
14686 .call_value(
14687 set_timeout,
14688 Value::UNDEFINED,
14689 &[callback, Value::int32(1), Value::int32(42)],
14690 )
14691 .unwrap();
14692 shared.borrow_mut().reports.push_back(TimerWakeup {
14693 id: 1,
14694 deadline_ms: 1,
14695 });
14696 machine.run_one_expired_timer().unwrap();
14697 assert_eq!(read_global(&machine, "this_seen"), Some(handle));
14698 assert_eq!(read_global(&machine, "arg_seen"), Some(Value::int32(42)));
14699 }
14700
14701 #[test]
14702 fn a_callback_created_timer_waits_for_a_later_checkpoint() {
14703 let program = timer_program();
14704 let mut host = TimerTestHost::default();
14705 let shared = host.provider.state.clone();
14706 let mut machine = Machine::new(&program, &mut host, Limits::default());
14707 machine.frames.clear();
14708 machine.live_registers = 0;
14709 let set_timeout = set_timeout_global(&machine);
14710 let nested = timer_fn(&mut machine, 2);
14711 machine
14712 .globals
14713 .insert(EcmaString::from_utf8("nestedCallback"), nested);
14714 let creator = timer_native(&mut machine, "schedule nested", schedule_nested_timer);
14715 machine
14716 .call_value(set_timeout, Value::UNDEFINED, &[creator, Value::int32(1)])
14717 .unwrap();
14718 shared.borrow_mut().reports.push_back(TimerWakeup {
14719 id: 1,
14720 deadline_ms: 1,
14721 });
14722 assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14723 assert_eq!(read_global(&machine, "b"), None);
14724 assert!(machine.has_pending_timers());
14725 shared.borrow_mut().reports.push_back(TimerWakeup {
14728 id: 2,
14729 deadline_ms: 1,
14730 });
14731 assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14732 assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14733 }
14734
14735 #[test]
14736 fn timer_callback_throw_is_reported_and_a_runtime_failure_propagates() {
14737 let program = timer_program();
14738 let mut host = TimerTestHost::default();
14739 let shared = host.provider.state.clone();
14740 let mut machine = Machine::new(&program, &mut host, Limits::default());
14741 machine.frames.clear();
14742 machine.live_registers = 0;
14743 let set_timeout = set_timeout_global(&machine);
14744 let thrower = timer_fn(&mut machine, 4);
14745 machine
14746 .call_value(set_timeout, Value::UNDEFINED, &[thrower, Value::int32(1)])
14747 .unwrap();
14748 shared.borrow_mut().reports.push_back(TimerWakeup {
14749 id: 1,
14750 deadline_ms: 1,
14751 });
14752 let run = machine.run_one_expired_timer().unwrap();
14753 assert_eq!(run.executed, 1);
14754 assert_eq!(
14755 run.uncaught,
14756 vec![CallbackException {
14757 value: Value::int32(7),
14758 origin: ThrowOrigin::Bytecode
14759 }]
14760 );
14761
14762 let another = timer_fn(&mut machine, 1);
14764 machine
14765 .call_value(set_timeout, Value::UNDEFINED, &[another, Value::int32(1)])
14766 .unwrap();
14767 shared.borrow_mut().reports.push_back(TimerWakeup {
14768 id: 2,
14769 deadline_ms: 1,
14770 });
14771 machine.fuel = 1;
14772 let error = machine.run_one_expired_timer().unwrap_err();
14773 assert!(matches!(error.kind, RuntimeErrorKind::FuelExhausted { .. }));
14774 }
14775
14776 #[test]
14777 fn a_timer_checkpoint_never_drains_microtasks() {
14778 let program = timer_program();
14779 let mut host = TimerTestHost::default();
14780 let shared = host.provider.state.clone();
14781 let mut machine = Machine::new(&program, &mut host, Limits::default());
14782 machine.frames.clear();
14783 machine.live_registers = 0;
14784 let set_timeout = set_timeout_global(&machine);
14785 let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14786 let a = timer_fn(&mut machine, 1);
14787 let b = timer_fn(&mut machine, 2);
14788 machine
14789 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14790 .unwrap();
14791 machine.call_value(queue, Value::UNDEFINED, &[b]).unwrap();
14792 shared.borrow_mut().reports.push_back(TimerWakeup {
14793 id: 1,
14794 deadline_ms: 1,
14795 });
14796 let run = machine.run_one_expired_timer().unwrap();
14797 assert_eq!(run.executed, 1);
14798 assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14799 assert_eq!(read_global(&machine, "b"), None);
14800 assert_eq!(machine.microtasks.len(), 1);
14801 machine.drain_microtasks().unwrap();
14802 assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14803 }
14804
14805 #[test]
14806 fn timer_reentry_capacity_and_fuel_preserve_state() {
14807 let program = timer_program();
14808 let mut host = TimerTestHost::default();
14809 let shared = host.provider.state.clone();
14810 let mut machine = Machine::new(
14811 &program,
14812 &mut host,
14813 Limits {
14814 max_timers: 1,
14815 ..Limits::default()
14816 },
14817 );
14818 machine.frames.clear();
14819 machine.live_registers = 0;
14820 let set_timeout = set_timeout_global(&machine);
14821 let a = timer_fn(&mut machine, 1);
14822 let b = timer_fn(&mut machine, 2);
14823 machine
14824 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14825 .unwrap();
14826 let capacity = machine
14828 .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(1)])
14829 .unwrap_err();
14830 assert!(matches!(
14831 capacity,
14832 EvalFailure::Runtime(RuntimeErrorKind::TimerCapacityExceeded { limit: 1 })
14833 ));
14834 assert_eq!(shared.borrow().scheduled.len(), 1);
14835
14836 shared.borrow_mut().reports.push_back(TimerWakeup {
14838 id: 1,
14839 deadline_ms: 1,
14840 });
14841 machine.timer_checkpoint_active = true;
14842 let fuel = machine.fuel;
14843 let reentry = machine.run_one_expired_timer().unwrap_err();
14844 assert!(matches!(
14845 reentry.kind,
14846 RuntimeErrorKind::TimerCheckpointReentry
14847 ));
14848 assert_eq!(machine.fuel, fuel);
14849 machine.timer_checkpoint_active = false;
14850
14851 machine.fuel = 0;
14853 let exhausted = machine.run_one_expired_timer().unwrap_err();
14854 assert!(matches!(
14855 exhausted.kind,
14856 RuntimeErrorKind::FuelExhausted { .. }
14857 ));
14858 assert!(machine.has_pending_timers());
14859 machine.fuel = 100;
14860 assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14861 assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14862 }
14863
14864 #[test]
14865 fn a_failed_schedule_never_reuses_its_timer_id() {
14866 let program = timer_program();
14867 let mut host = TimerTestHost::default();
14868 let shared = host.provider.state.clone();
14869 let mut machine = Machine::new(&program, &mut host, Limits::default());
14870 machine.frames.clear();
14871 machine.live_registers = 0;
14872 let set_timeout = set_timeout_global(&machine);
14873 let a = timer_fn(&mut machine, 1);
14874 shared.borrow_mut().fail_schedule = true;
14875 let failure = machine
14876 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14877 .unwrap_err();
14878 assert!(matches!(
14879 failure,
14880 EvalFailure::Runtime(RuntimeErrorKind::TimerProviderFailure { .. })
14881 ));
14882 shared.borrow_mut().fail_schedule = false;
14883 machine
14884 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14885 .unwrap();
14886 let ids: Vec<u64> = shared
14887 .borrow()
14888 .scheduled
14889 .iter()
14890 .map(|(id, _)| *id)
14891 .collect();
14892 assert_eq!(ids, vec![1, 2]);
14893 }
14894
14895 #[test]
14896 fn wait_for_timer_expiry_promotes_a_reported_timer() {
14897 let program = timer_program();
14898 let mut host = TimerTestHost::default();
14899 let shared = host.provider.state.clone();
14900 let mut machine = Machine::new(&program, &mut host, Limits::default());
14901 machine.frames.clear();
14902 machine.live_registers = 0;
14903 assert!(!machine.wait_for_timer_expiry().unwrap());
14904 let set_timeout = set_timeout_global(&machine);
14905 let a = timer_fn(&mut machine, 1);
14906 machine
14907 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14908 .unwrap();
14909 shared.borrow_mut().reports.push_back(TimerWakeup {
14910 id: 1,
14911 deadline_ms: 1,
14912 });
14913 assert!(machine.wait_for_timer_expiry().unwrap());
14914 assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14915 assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14916 }
14917}