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 drain_microtasks(&mut self) -> Result<MicrotaskDrain, RuntimeError> {
1675 if self.microtask_drain_active {
1676 return Err(self.checkpoint_error(RuntimeErrorKind::MicrotaskDrainReentry));
1677 }
1678 self.microtask_drain_active = true;
1679 let result = (|| {
1680 let mut report = MicrotaskDrain::default();
1681 while self.microtasks.front().is_some() {
1682 self.consume_fuel(1)
1683 .map_err(|kind| self.checkpoint_error(kind))?;
1684 let job = self
1685 .microtasks
1686 .pop_front()
1687 .expect("the queued microtask remains present after fuel charging");
1688 report.executed = report.executed.saturating_add(1);
1689 self.execute_microtask_job(job, &mut report)
1690 .map_err(|kind| self.checkpoint_error(kind))?;
1691 }
1692 Ok(report)
1693 })();
1694 self.microtask_drain_active = false;
1695 result
1696 }
1697
1698 fn checkpoint_error(&self, kind: RuntimeErrorKind) -> RuntimeError {
1699 let function = self.module.entry();
1700 let instruction = self.module.functions()[function.get() as usize]
1701 .code()
1702 .first()
1703 .copied()
1704 .unwrap_or(Instruction::Halt);
1705 RuntimeError {
1706 kind,
1707 function,
1708 pc: Pc::new(0),
1709 source: RuntimeSource {
1710 function_name: None,
1711 instruction,
1712 },
1713 }
1714 }
1715
1716 fn execute_microtask_job(
1717 &mut self,
1718 job: MicrotaskJob,
1719 report: &mut MicrotaskDrain,
1720 ) -> Result<(), RuntimeErrorKind> {
1721 match job {
1722 MicrotaskJob::Reaction {
1723 reaction,
1724 value,
1725 origin,
1726 } => self.execute_promise_reaction(reaction, value, origin),
1727 MicrotaskJob::Thenable {
1728 promise,
1729 thenable,
1730 then,
1731 } => self.execute_thenable_job(promise, thenable, then),
1732 MicrotaskJob::Callback { callback } => {
1733 self.execute_callback_microtask(callback, report)
1734 }
1735 }
1736 }
1737
1738 fn execute_callback_microtask(
1739 &mut self,
1740 callback: Value,
1741 report: &mut MicrotaskDrain,
1742 ) -> Result<(), RuntimeErrorKind> {
1743 match self.call_value(callback, Value::UNDEFINED, &[]) {
1744 Ok(_) => Ok(()),
1745 Err(EvalFailure::Runtime(kind)) => Err(kind),
1746 Err(failure) => {
1747 let (value, origin) =
1748 self.promise_rejection_value(failure)
1749 .map_err(|failure| match failure {
1750 EvalFailure::Runtime(kind) => kind,
1751 _ => RuntimeErrorKind::InvalidValue { value: callback },
1752 })?;
1753 report.uncaught.try_reserve(1).map_err(|_| {
1754 RuntimeErrorKind::HeapByteLimitExceeded {
1755 limit: self.limits.max_heap_bytes,
1756 }
1757 })?;
1758 report.uncaught.push(CallbackException { value, origin });
1759 Ok(())
1760 }
1761 }
1762 }
1763
1764 fn execute_thenable_job(
1765 &mut self,
1766 promise: Value,
1767 thenable: Value,
1768 then: Value,
1769 ) -> Result<(), RuntimeErrorKind> {
1770 let record = self
1771 .create_promise_resolver(promise)
1772 .map_err(|failure| match failure {
1773 EvalFailure::Runtime(kind) => kind,
1774 _ => RuntimeErrorKind::InvalidValue { value: promise },
1775 })?;
1776 let (resolve_target, reject_target) = self.intrinsics.builtins.promise_resolver_targets();
1777 let resolve = self
1778 .create_promise_resolver_function(resolve_target, record)
1779 .map_err(|failure| match failure {
1780 EvalFailure::Runtime(kind) => kind,
1781 _ => RuntimeErrorKind::InvalidValue { value: record },
1782 })?;
1783 let reject = self
1784 .create_promise_resolver_function(reject_target, record)
1785 .map_err(|failure| match failure {
1786 EvalFailure::Runtime(kind) => kind,
1787 _ => RuntimeErrorKind::InvalidValue { value: record },
1788 })?;
1789 match self.call_value(then, thenable, &[resolve, reject]) {
1790 Ok(_) => Ok(()),
1791 Err(EvalFailure::Runtime(kind)) => Err(kind),
1792 Err(failure) => self
1793 .reject_promise_resolver_failure(record, failure)
1794 .map_err(|failure| match failure {
1795 EvalFailure::Runtime(kind) => kind,
1796 _ => RuntimeErrorKind::InvalidValue { value: record },
1797 }),
1798 }
1799 }
1800
1801 fn execute_promise_reaction(
1802 &mut self,
1803 reaction: PromiseReaction,
1804 value: Value,
1805 origin: ThrowOrigin,
1806 ) -> Result<(), RuntimeErrorKind> {
1807 match reaction {
1808 PromiseReaction::Fulfilled { handler, derived } => self.execute_promise_handler(
1809 handler,
1810 derived,
1811 value,
1812 origin,
1813 PromiseCompletion::Fulfilled,
1814 ),
1815 PromiseReaction::Rejected { handler, derived } => self.execute_promise_handler(
1816 handler,
1817 derived,
1818 value,
1819 origin,
1820 PromiseCompletion::Rejected,
1821 ),
1822 PromiseReaction::Finally {
1823 handler,
1824 derived,
1825 completion,
1826 } => self.execute_promise_finally(handler, derived, value, origin, completion),
1827 PromiseReaction::AsyncFulfill { activation } => {
1828 self.resume_async(activation, value, None)
1829 }
1830 PromiseReaction::AsyncReject { activation } => {
1831 self.resume_async(activation, value, Some(origin))
1832 }
1833 }
1834 }
1835
1836 fn execute_promise_handler(
1837 &mut self,
1838 handler: Value,
1839 derived: Value,
1840 value: Value,
1841 origin: ThrowOrigin,
1842 completion: PromiseCompletion,
1843 ) -> Result<(), RuntimeErrorKind> {
1844 if !self.is_callable(handler).map_err(|failure| match failure {
1845 EvalFailure::Runtime(kind) => kind,
1846 _ => RuntimeErrorKind::InvalidValue { value: handler },
1847 })? {
1848 return match completion {
1849 PromiseCompletion::Fulfilled => self.resolve_promise(derived, value),
1850 PromiseCompletion::Rejected => self.reject_promise(derived, value, origin),
1851 };
1852 }
1853 match self.call_value(handler, Value::UNDEFINED, &[value]) {
1854 Ok(result) => self.resolve_promise(derived, result),
1855 Err(EvalFailure::Runtime(kind)) => Err(kind),
1856 Err(failure) => self
1857 .reject_promise_failure(derived, failure)
1858 .map_err(|failure| match failure {
1859 EvalFailure::Runtime(kind) => kind,
1860 _ => RuntimeErrorKind::InvalidValue { value: derived },
1861 }),
1862 }
1863 }
1864
1865 fn execute_promise_finally(
1866 &mut self,
1867 handler: Value,
1868 derived: Value,
1869 value: Value,
1870 origin: ThrowOrigin,
1871 completion: PromiseCompletion,
1872 ) -> Result<(), RuntimeErrorKind> {
1873 if !self.is_callable(handler).map_err(|failure| match failure {
1874 EvalFailure::Runtime(kind) => kind,
1875 _ => RuntimeErrorKind::InvalidValue { value: handler },
1876 })? {
1877 return match completion {
1878 PromiseCompletion::Fulfilled => self.resolve_promise(derived, value),
1879 PromiseCompletion::Rejected => self.reject_promise(derived, value, origin),
1880 };
1881 }
1882 let cleanup = self.create_promise().map_err(|failure| match failure {
1883 EvalFailure::Runtime(kind) => kind,
1884 _ => RuntimeErrorKind::InvalidValue { value: derived },
1885 })?;
1886 let record = self
1887 .create_promise_finally(derived, value, origin, completion)
1888 .map_err(|failure| match failure {
1889 EvalFailure::Runtime(kind) => kind,
1890 _ => RuntimeErrorKind::InvalidValue { value: derived },
1891 })?;
1892 let (on_fulfilled, on_rejected) = self.intrinsics.builtins.promise_finally_targets();
1893 let on_fulfilled = self
1894 .create_promise_resolver_function(on_fulfilled, record)
1895 .map_err(|failure| match failure {
1896 EvalFailure::Runtime(kind) => kind,
1897 _ => RuntimeErrorKind::InvalidValue { value: record },
1898 })?;
1899 let on_rejected = self
1900 .create_promise_resolver_function(on_rejected, record)
1901 .map_err(|failure| match failure {
1902 EvalFailure::Runtime(kind) => kind,
1903 _ => RuntimeErrorKind::InvalidValue { value: record },
1904 })?;
1905 self.promise_then(cleanup, on_fulfilled, on_rejected)
1906 .map_err(|failure| match failure {
1907 EvalFailure::Runtime(kind) => kind,
1908 _ => RuntimeErrorKind::InvalidValue { value: cleanup },
1909 })?;
1910 match self.call_value(handler, Value::UNDEFINED, &[]) {
1911 Ok(result) => self.resolve_promise(cleanup, result),
1912 Err(EvalFailure::Runtime(kind)) => Err(kind),
1913 Err(failure) => self
1914 .reject_promise_failure(cleanup, failure)
1915 .map_err(|failure| match failure {
1916 EvalFailure::Runtime(kind) => kind,
1917 _ => RuntimeErrorKind::InvalidValue { value: cleanup },
1918 }),
1919 }
1920 }
1921
1922 pub(crate) fn enqueue_microtask_callback(
1923 &mut self,
1924 callback: Value,
1925 ) -> Result<(), EvalFailure> {
1926 self.ensure_microtask_capacity(1)
1927 .map_err(EvalFailure::Runtime)?;
1928 self.microtasks
1929 .push_back(MicrotaskJob::Callback { callback });
1930 Ok(())
1931 }
1932
1933 fn ensure_microtask_capacity(&mut self, additional: usize) -> Result<(), RuntimeErrorKind> {
1934 if self
1935 .microtasks
1936 .len()
1937 .checked_add(additional)
1938 .is_none_or(|length| length > self.limits.max_microtasks)
1939 {
1940 return Err(RuntimeErrorKind::MicrotaskQueueLimitExceeded {
1941 limit: self.limits.max_microtasks,
1942 });
1943 }
1944 self.microtasks.try_reserve(additional).map_err(|_| {
1945 RuntimeErrorKind::HeapByteLimitExceeded {
1946 limit: self.limits.max_heap_bytes,
1947 }
1948 })
1949 }
1950
1951 pub(crate) fn create_promise(&mut self) -> Result<Value, EvalFailure> {
1952 self.allocate(HeapEntry::Promise {
1953 state: PromiseState::Pending {
1954 fulfill_reactions: Vec::new(),
1955 reject_reactions: Vec::new(),
1956 },
1957 properties: PropertyMap::default(),
1958 prototype: Some(self.intrinsics.builtins.promise_prototype()),
1959 extensible: true,
1960 })
1961 .map_err(EvalFailure::Runtime)
1962 }
1963
1964 pub(crate) fn create_promise_resolver(&mut self, promise: Value) -> Result<Value, EvalFailure> {
1965 self.allocate(HeapEntry::PromiseResolver {
1966 promise,
1967 used: false,
1968 })
1969 .map_err(EvalFailure::Runtime)
1970 }
1971
1972 pub(crate) fn create_promise_resolver_function(
1973 &mut self,
1974 target: Value,
1975 record: Value,
1976 ) -> Result<Value, EvalFailure> {
1977 self.allocate(HeapEntry::NativeFunction {
1978 callable: NativeCallable::Bound(Box::new(BoundCallable {
1979 target,
1980 this_value: Value::UNDEFINED,
1981 arguments: vec![record],
1982 })),
1983 properties: PropertyMap::default(),
1984 extensible: true,
1985 })
1986 .map_err(EvalFailure::Runtime)
1987 }
1988
1989 pub(crate) fn resolve_promise_resolver(
1990 &mut self,
1991 record: Value,
1992 value: Value,
1993 ) -> Result<(), EvalFailure> {
1994 if let Some(promise) = self.use_promise_resolver(record)? {
1995 self.resolve_promise(promise, value)
1996 .map_err(EvalFailure::Runtime)?;
1997 }
1998 Ok(())
1999 }
2000
2001 pub(crate) fn reject_promise_resolver(
2002 &mut self,
2003 record: Value,
2004 reason: Value,
2005 ) -> Result<(), EvalFailure> {
2006 if let Some(promise) = self.use_promise_resolver(record)? {
2007 self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2008 .map_err(EvalFailure::Runtime)?;
2009 }
2010 Ok(())
2011 }
2012
2013 pub(crate) fn reject_promise_resolver_failure(
2014 &mut self,
2015 record: Value,
2016 failure: EvalFailure,
2017 ) -> Result<(), EvalFailure> {
2018 if let Some(promise) = self.use_promise_resolver(record)? {
2019 self.reject_promise_failure(promise, failure)?;
2020 }
2021 Ok(())
2022 }
2023
2024 fn use_promise_resolver(&mut self, record: Value) -> Result<Option<Value>, EvalFailure> {
2025 let index = self
2026 .runtime_slot(record)
2027 .map_err(EvalFailure::Runtime)?
2028 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2029 operation: "Promise resolver",
2030 }))?;
2031 let HeapEntry::PromiseResolver { promise, used } = &mut self.heap[index] else {
2032 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2033 operation: "Promise resolver",
2034 }));
2035 };
2036 if *used {
2037 return Ok(None);
2038 }
2039 *used = true;
2040 Ok(Some(*promise))
2041 }
2042
2043 fn charge_promise_reactions(&mut self, count: usize) -> Result<(), EvalFailure> {
2044 let bytes = std::mem::size_of::<PromiseReaction>()
2045 .checked_mul(count)
2046 .ok_or(EvalFailure::Runtime(
2047 RuntimeErrorKind::HeapByteLimitExceeded {
2048 limit: self.limits.max_heap_bytes,
2049 },
2050 ))?;
2051 self.charge_heap(bytes).map_err(EvalFailure::Runtime)
2052 }
2053
2054 pub(crate) fn promise_then(
2055 &mut self,
2056 promise: Value,
2057 on_fulfilled: Value,
2058 on_rejected: Value,
2059 ) -> Result<Value, EvalFailure> {
2060 let index = self
2061 .runtime_slot(promise)
2062 .map_err(EvalFailure::Runtime)?
2063 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2064 operation: "Promise.prototype.then",
2065 }))?;
2066 let settled = match &self.heap[index] {
2067 HeapEntry::Promise {
2068 state: PromiseState::Pending { .. },
2069 ..
2070 } => None,
2071 HeapEntry::Promise {
2072 state: PromiseState::Fulfilled { value },
2073 ..
2074 } => Some((true, *value, ThrowOrigin::Bytecode)),
2075 HeapEntry::Promise {
2076 state: PromiseState::Rejected { reason, origin },
2077 ..
2078 } => Some((false, *reason, *origin)),
2079 _ => {
2080 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2081 operation: "Promise.prototype.then",
2082 }));
2083 }
2084 };
2085 let derived = self.create_promise()?;
2086 if let Some((fulfilled, value, origin)) = settled {
2087 self.ensure_microtask_capacity(1)
2088 .map_err(EvalFailure::Runtime)?;
2089 let reaction = if fulfilled {
2090 PromiseReaction::Fulfilled {
2091 handler: on_fulfilled,
2092 derived,
2093 }
2094 } else {
2095 PromiseReaction::Rejected {
2096 handler: on_rejected,
2097 derived,
2098 }
2099 };
2100 self.microtasks.push_back(MicrotaskJob::Reaction {
2101 reaction,
2102 value,
2103 origin,
2104 });
2105 return Ok(derived);
2106 }
2107 self.charge_promise_reactions(2)?;
2108 let HeapEntry::Promise {
2109 state:
2110 PromiseState::Pending {
2111 fulfill_reactions,
2112 reject_reactions,
2113 },
2114 ..
2115 } = &mut self.heap[index]
2116 else {
2117 unreachable!("pending Promise state was checked before derived allocation");
2118 };
2119 fulfill_reactions.push(PromiseReaction::Fulfilled {
2120 handler: on_fulfilled,
2121 derived,
2122 });
2123 reject_reactions.push(PromiseReaction::Rejected {
2124 handler: on_rejected,
2125 derived,
2126 });
2127 Ok(derived)
2128 }
2129
2130 pub(crate) fn promise_finally(
2131 &mut self,
2132 promise: Value,
2133 handler: Value,
2134 ) -> Result<Value, EvalFailure> {
2135 let index = self
2136 .runtime_slot(promise)
2137 .map_err(EvalFailure::Runtime)?
2138 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2139 operation: "Promise.prototype.finally",
2140 }))?;
2141 let settled = match &self.heap[index] {
2142 HeapEntry::Promise {
2143 state: PromiseState::Pending { .. },
2144 ..
2145 } => None,
2146 HeapEntry::Promise {
2147 state: PromiseState::Fulfilled { value },
2148 ..
2149 } => Some((true, *value, ThrowOrigin::Bytecode)),
2150 HeapEntry::Promise {
2151 state: PromiseState::Rejected { reason, origin },
2152 ..
2153 } => Some((false, *reason, *origin)),
2154 _ => {
2155 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2156 operation: "Promise.prototype.finally",
2157 }));
2158 }
2159 };
2160 let derived = self.create_promise()?;
2161 let reaction = |completion| PromiseReaction::Finally {
2162 handler,
2163 derived,
2164 completion,
2165 };
2166 if let Some((fulfilled, value, origin)) = settled {
2167 self.ensure_microtask_capacity(1)
2168 .map_err(EvalFailure::Runtime)?;
2169 self.microtasks.push_back(MicrotaskJob::Reaction {
2170 reaction: reaction(if fulfilled {
2171 PromiseCompletion::Fulfilled
2172 } else {
2173 PromiseCompletion::Rejected
2174 }),
2175 value,
2176 origin,
2177 });
2178 return Ok(derived);
2179 }
2180 self.charge_promise_reactions(2)?;
2181 let HeapEntry::Promise {
2182 state:
2183 PromiseState::Pending {
2184 fulfill_reactions,
2185 reject_reactions,
2186 },
2187 ..
2188 } = &mut self.heap[index]
2189 else {
2190 unreachable!("pending Promise state was checked before derived allocation");
2191 };
2192 fulfill_reactions.push(reaction(PromiseCompletion::Fulfilled));
2193 reject_reactions.push(reaction(PromiseCompletion::Rejected));
2194 Ok(derived)
2195 }
2196
2197 pub(crate) fn create_promise_finally(
2198 &mut self,
2199 derived: Value,
2200 value: Value,
2201 origin: ThrowOrigin,
2202 completion: PromiseCompletion,
2203 ) -> Result<Value, EvalFailure> {
2204 self.allocate(HeapEntry::PromiseFinally {
2205 derived,
2206 value,
2207 origin,
2208 completion,
2209 })
2210 .map_err(EvalFailure::Runtime)
2211 }
2212
2213 pub(crate) fn fulfill_promise_finally(&mut self, record: Value) -> Result<(), EvalFailure> {
2214 let (derived, value, origin, completion) = self.promise_finally_record(record)?;
2215 match completion {
2216 PromiseCompletion::Fulfilled => self
2217 .resolve_promise(derived, value)
2218 .map_err(EvalFailure::Runtime),
2219 PromiseCompletion::Rejected => self
2220 .reject_promise(derived, value, origin)
2221 .map_err(EvalFailure::Runtime),
2222 }
2223 }
2224
2225 pub(crate) fn reject_promise_finally(
2226 &mut self,
2227 record: Value,
2228 reason: Value,
2229 ) -> Result<(), EvalFailure> {
2230 let (derived, _, _, _) = self.promise_finally_record(record)?;
2231 self.reject_promise(derived, reason, ThrowOrigin::Bytecode)
2232 .map_err(EvalFailure::Runtime)
2233 }
2234
2235 fn promise_finally_record(
2236 &mut self,
2237 record: Value,
2238 ) -> Result<(Value, Value, ThrowOrigin, PromiseCompletion), EvalFailure> {
2239 let index = self
2240 .runtime_slot(record)
2241 .map_err(EvalFailure::Runtime)?
2242 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2243 operation: "Promise finally target",
2244 }))?;
2245 let HeapEntry::PromiseFinally {
2246 derived,
2247 value,
2248 origin,
2249 completion,
2250 } = &self.heap[index]
2251 else {
2252 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2253 operation: "Promise finally target",
2254 }));
2255 };
2256 Ok((*derived, *value, *origin, *completion))
2257 }
2258
2259 pub(crate) fn promise_resolve(&mut self, value: Value) -> Result<Value, EvalFailure> {
2260 if matches!(self.runtime_slot(value).map_err(EvalFailure::Runtime)?, Some(index) if matches!(self.heap[index], HeapEntry::Promise { .. }))
2261 {
2262 return Ok(value);
2263 }
2264 let promise = self.create_promise()?;
2265 self.resolve_promise(promise, value)
2266 .map_err(EvalFailure::Runtime)?;
2267 Ok(promise)
2268 }
2269
2270 pub(crate) fn promise_reject(&mut self, reason: Value) -> Result<Value, EvalFailure> {
2271 let promise = self.create_promise()?;
2272 self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2273 .map_err(EvalFailure::Runtime)?;
2274 Ok(promise)
2275 }
2276
2277 pub(crate) fn promise_all(&mut self, iterable: Value) -> Result<Value, EvalFailure> {
2278 let promise = self.create_promise()?;
2279 let aggregate = self
2280 .allocate(HeapEntry::PromiseAll {
2281 promise,
2282 values: Vec::new(),
2283 remaining: 1,
2284 settled: false,
2285 })
2286 .map_err(EvalFailure::Runtime)?;
2287 let iterator = match self.create_iterator(iterable, IteratorKind::Sync) {
2288 Ok(iterator) => iterator,
2289 Err(failure) => {
2290 self.mark_promise_all_settled(aggregate)?;
2291 self.reject_promise_failure(promise, failure)?;
2292 return Ok(promise);
2293 }
2294 };
2295 loop {
2296 let value = match self.iterator_next(iterator) {
2297 Ok((true, _)) => break,
2298 Ok((false, value)) => value,
2299 Err(failure) => {
2300 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2301 }
2302 };
2303 let index = match self.add_promise_all_element(aggregate) {
2304 Ok(index) => index,
2305 Err(failure) => {
2306 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2307 }
2308 };
2309 let element = match self
2310 .allocate(HeapEntry::PromiseAllElement {
2311 aggregate,
2312 index,
2313 called: false,
2314 })
2315 .map_err(EvalFailure::Runtime)
2316 {
2317 Ok(element) => element,
2318 Err(failure) => {
2319 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2320 }
2321 };
2322 let (fulfill_target, reject_target) = self.intrinsics.builtins.promise_all_targets();
2323 let on_fulfilled = match self.create_promise_resolver_function(fulfill_target, element)
2324 {
2325 Ok(callback) => callback,
2326 Err(failure) => {
2327 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2328 }
2329 };
2330 let on_rejected = match self.create_promise_resolver_function(reject_target, element) {
2331 Ok(callback) => callback,
2332 Err(failure) => {
2333 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2334 }
2335 };
2336 let resolved = match self.promise_resolve(value) {
2337 Ok(resolved) => resolved,
2338 Err(failure) => {
2339 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2340 }
2341 };
2342 if let Err(failure) = self.promise_then(resolved, on_fulfilled, on_rejected) {
2343 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2344 }
2345 }
2346 if let Some(values) = self.finish_promise_all(aggregate)? {
2347 let array = self.create_array(values)?;
2348 self.fulfill_promise(promise, array)
2349 .map_err(EvalFailure::Runtime)?;
2350 }
2351 Ok(promise)
2352 }
2353
2354 fn reject_promise_all_abrupt(
2355 &mut self,
2356 aggregate: Value,
2357 promise: Value,
2358 iterator: Value,
2359 failure: EvalFailure,
2360 ) -> Result<Value, EvalFailure> {
2361 self.mark_promise_all_settled(aggregate)?;
2362 if let Err(EvalFailure::Runtime(kind)) = self.close_iterator(iterator) {
2363 return Err(EvalFailure::Runtime(kind));
2364 }
2365 self.reject_promise_failure(promise, failure)?;
2366 Ok(promise)
2367 }
2368
2369 fn close_iterator(&mut self, iterator: Value) -> Result<(), EvalFailure> {
2370 let Some(index) = self.runtime_slot(iterator).map_err(EvalFailure::Runtime)? else {
2371 return Ok(());
2372 };
2373 let HeapEntry::Iterator {
2374 state: IteratorState::Protocol { iterator, .. },
2375 } = &self.heap[index]
2376 else {
2377 return Ok(());
2378 };
2379 let iterator = *iterator;
2380 let close = self.get_named_property(iterator, "return")?;
2381 if self.is_callable(close)? {
2382 let _ = self.call_value(close, iterator, &[])?;
2383 }
2384 Ok(())
2385 }
2386
2387 fn mark_promise_all_settled(&mut self, aggregate: Value) -> Result<bool, EvalFailure> {
2388 let index = self
2389 .runtime_slot(aggregate)
2390 .map_err(EvalFailure::Runtime)?
2391 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2392 operation: "Promise.all target",
2393 }))?;
2394 let HeapEntry::PromiseAll { settled, .. } = &mut self.heap[index] else {
2395 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2396 operation: "Promise.all target",
2397 }));
2398 };
2399 let changed = !*settled;
2400 *settled = true;
2401 Ok(changed)
2402 }
2403
2404 fn add_promise_all_element(&mut self, aggregate: Value) -> Result<usize, EvalFailure> {
2405 let index = self
2406 .runtime_slot(aggregate)
2407 .map_err(EvalFailure::Runtime)?
2408 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2409 operation: "Promise.all target",
2410 }))?;
2411 let next_remaining = match &self.heap[index] {
2412 HeapEntry::PromiseAll {
2413 remaining,
2414 settled: false,
2415 ..
2416 } => remaining.checked_add(1).ok_or(EvalFailure::Runtime(
2417 RuntimeErrorKind::HeapByteLimitExceeded {
2418 limit: self.limits.max_heap_bytes,
2419 },
2420 ))?,
2421 HeapEntry::PromiseAll { .. } => {
2422 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2423 operation: "Promise.all target",
2424 }));
2425 }
2426 _ => {
2427 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2428 operation: "Promise.all target",
2429 }));
2430 }
2431 };
2432 self.charge_heap(std::mem::size_of::<Value>())
2433 .map_err(EvalFailure::Runtime)?;
2434 let HeapEntry::PromiseAll {
2435 values, remaining, ..
2436 } = &mut self.heap[index]
2437 else {
2438 unreachable!("Promise.all aggregate was checked before its heap charge");
2439 };
2440 values.try_reserve(1).map_err(|_| {
2441 EvalFailure::Runtime(RuntimeErrorKind::HeapByteLimitExceeded {
2442 limit: self.limits.max_heap_bytes,
2443 })
2444 })?;
2445 let index = values.len();
2446 values.push(Value::UNDEFINED);
2447 *remaining = next_remaining;
2448 Ok(index)
2449 }
2450
2451 fn finish_promise_all(&mut self, aggregate: Value) -> Result<Option<Vec<Value>>, EvalFailure> {
2452 let index = self
2453 .runtime_slot(aggregate)
2454 .map_err(EvalFailure::Runtime)?
2455 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2456 operation: "Promise.all target",
2457 }))?;
2458 let HeapEntry::PromiseAll {
2459 values,
2460 remaining,
2461 settled,
2462 ..
2463 } = &mut self.heap[index]
2464 else {
2465 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2466 operation: "Promise.all target",
2467 }));
2468 };
2469 if *settled {
2470 return Ok(None);
2471 }
2472 *remaining -= 1;
2473 if *remaining != 0 {
2474 return Ok(None);
2475 }
2476 *settled = true;
2477 Ok(Some(std::mem::take(values)))
2478 }
2479
2480 pub(crate) fn resolve_promise_all_element(
2481 &mut self,
2482 element: Value,
2483 value: Value,
2484 ) -> Result<(), EvalFailure> {
2485 let index = self
2486 .runtime_slot(element)
2487 .map_err(EvalFailure::Runtime)?
2488 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2489 operation: "Promise.all target",
2490 }))?;
2491 let (aggregate, output_index) = {
2492 let HeapEntry::PromiseAllElement {
2493 aggregate,
2494 index: output_index,
2495 called,
2496 } = &mut self.heap[index]
2497 else {
2498 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2499 operation: "Promise.all target",
2500 }));
2501 };
2502 if *called {
2503 return Ok(());
2504 }
2505 *called = true;
2506 (*aggregate, *output_index)
2507 };
2508 let aggregate_index = self
2509 .runtime_slot(aggregate)
2510 .map_err(EvalFailure::Runtime)?
2511 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2512 operation: "Promise.all target",
2513 }))?;
2514 let (promise, values) = {
2515 let HeapEntry::PromiseAll {
2516 promise,
2517 values,
2518 remaining,
2519 settled,
2520 } = &mut self.heap[aggregate_index]
2521 else {
2522 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2523 operation: "Promise.all target",
2524 }));
2525 };
2526 if *settled {
2527 return Ok(());
2528 }
2529 values[output_index] = value;
2530 *remaining -= 1;
2531 let values = (*remaining == 0).then(|| {
2532 *settled = true;
2533 std::mem::take(values)
2534 });
2535 (*promise, values)
2536 };
2537 if let Some(values) = values {
2538 let array = self.create_array(values)?;
2539 self.fulfill_promise(promise, array)
2540 .map_err(EvalFailure::Runtime)?;
2541 }
2542 Ok(())
2543 }
2544
2545 pub(crate) fn reject_promise_all_element(
2546 &mut self,
2547 element: Value,
2548 reason: Value,
2549 ) -> Result<(), EvalFailure> {
2550 let index = self
2551 .runtime_slot(element)
2552 .map_err(EvalFailure::Runtime)?
2553 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2554 operation: "Promise.all target",
2555 }))?;
2556 let aggregate = {
2557 let HeapEntry::PromiseAllElement {
2558 aggregate, called, ..
2559 } = &mut self.heap[index]
2560 else {
2561 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2562 operation: "Promise.all target",
2563 }));
2564 };
2565 if *called {
2566 return Ok(());
2567 }
2568 *called = true;
2569 *aggregate
2570 };
2571 let aggregate_index = self
2572 .runtime_slot(aggregate)
2573 .map_err(EvalFailure::Runtime)?
2574 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2575 operation: "Promise.all target",
2576 }))?;
2577 let HeapEntry::PromiseAll { promise, .. } = &self.heap[aggregate_index] else {
2578 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2579 operation: "Promise.all target",
2580 }));
2581 };
2582 let promise = *promise;
2583 if !self.mark_promise_all_settled(aggregate)? {
2584 return Ok(());
2585 }
2586 self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2587 .map_err(EvalFailure::Runtime)
2588 }
2589
2590 fn create_array(&mut self, elements: Vec<Value>) -> Result<Value, EvalFailure> {
2591 self.allocate(HeapEntry::Array {
2592 elements,
2593 properties: PropertyMap::default(),
2594 prototype: Some(self.intrinsics.array_prototype),
2595 extensible: true,
2596 length_writable: true,
2597 })
2598 .map_err(EvalFailure::Runtime)
2599 }
2600
2601 fn resolve_promise(&mut self, promise: Value, value: Value) -> Result<(), RuntimeErrorKind> {
2602 if promise == value {
2603 return self
2604 .reject_promise_failure(
2605 promise,
2606 EvalFailure::Throw(ThrowOrigin::TypeError {
2607 operation: "Promise cannot resolve itself",
2608 }),
2609 )
2610 .map_err(|failure| match failure {
2611 EvalFailure::Runtime(kind) => kind,
2612 _ => RuntimeErrorKind::InvalidValue { value: promise },
2613 });
2614 }
2615 if !self.is_object(value) {
2616 return self.fulfill_promise(promise, value);
2617 }
2618 let then = match self.get_named_property(value, "then") {
2619 Ok(then) => then,
2620 Err(EvalFailure::Runtime(kind)) => return Err(kind),
2621 Err(failure) => {
2622 return self.reject_promise_failure(promise, failure).map_err(
2623 |failure| match failure {
2624 EvalFailure::Runtime(kind) => kind,
2625 _ => RuntimeErrorKind::InvalidValue { value: promise },
2626 },
2627 );
2628 }
2629 };
2630 if !self.is_callable(then).map_err(|failure| match failure {
2631 EvalFailure::Runtime(kind) => kind,
2632 _ => RuntimeErrorKind::InvalidValue { value: then },
2633 })? {
2634 return self.fulfill_promise(promise, value);
2635 }
2636 self.ensure_microtask_capacity(1)?;
2637 self.microtasks.push_back(MicrotaskJob::Thenable {
2638 promise,
2639 thenable: value,
2640 then,
2641 });
2642 Ok(())
2643 }
2644
2645 fn reject_promise(
2646 &mut self,
2647 promise: Value,
2648 reason: Value,
2649 origin: ThrowOrigin,
2650 ) -> Result<(), RuntimeErrorKind> {
2651 self.settle_promise(promise, PromiseState::Rejected { reason, origin })
2652 }
2653
2654 fn fulfill_promise(&mut self, promise: Value, value: Value) -> Result<(), RuntimeErrorKind> {
2655 self.settle_promise(promise, PromiseState::Fulfilled { value })
2656 }
2657
2658 fn settle_promise(
2659 &mut self,
2660 promise: Value,
2661 terminal: PromiseState,
2662 ) -> Result<(), RuntimeErrorKind> {
2663 let index = self
2664 .runtime_slot(promise)?
2665 .ok_or(RuntimeErrorKind::InvalidValue { value: promise })?;
2666 let reaction_count = match &self.heap[index] {
2667 HeapEntry::Promise {
2668 state:
2669 PromiseState::Pending {
2670 fulfill_reactions,
2671 reject_reactions,
2672 },
2673 ..
2674 } => match &terminal {
2675 PromiseState::Fulfilled { .. } => fulfill_reactions.len(),
2676 PromiseState::Rejected { .. } => reject_reactions.len(),
2677 PromiseState::Pending { .. } => unreachable!("Promise settlement is terminal"),
2678 },
2679 HeapEntry::Promise { .. } => return Ok(()),
2680 _ => return Err(RuntimeErrorKind::InvalidValue { value: promise }),
2681 };
2682 self.ensure_microtask_capacity(reaction_count)?;
2683 let reactions = match &mut self.heap[index] {
2684 HeapEntry::Promise { state, .. } => {
2685 let reactions = match state {
2686 PromiseState::Pending {
2687 fulfill_reactions,
2688 reject_reactions,
2689 } => match &terminal {
2690 PromiseState::Fulfilled { .. } => std::mem::take(fulfill_reactions),
2691 PromiseState::Rejected { .. } => std::mem::take(reject_reactions),
2692 PromiseState::Pending { .. } => {
2693 unreachable!("Promise settlement is terminal")
2694 }
2695 },
2696 _ => return Ok(()),
2697 };
2698 *state = terminal.clone();
2699 reactions
2700 }
2701 _ => return Err(RuntimeErrorKind::InvalidValue { value: promise }),
2702 };
2703 let (value, origin) = match terminal {
2704 PromiseState::Fulfilled { value } => (value, ThrowOrigin::Bytecode),
2705 PromiseState::Rejected { reason, origin } => (reason, origin),
2706 PromiseState::Pending { .. } => unreachable!("Promise settlement is terminal"),
2707 };
2708 for reaction in reactions {
2709 self.microtasks.push_back(MicrotaskJob::Reaction {
2710 reaction,
2711 value,
2712 origin,
2713 });
2714 }
2715 Ok(())
2716 }
2717
2718 fn reject_promise_failure(
2719 &mut self,
2720 promise: Value,
2721 failure: EvalFailure,
2722 ) -> Result<(), EvalFailure> {
2723 let (reason, origin) = self.promise_rejection_value(failure)?;
2724 self.reject_promise(promise, reason, origin)
2725 .map_err(EvalFailure::Runtime)
2726 }
2727
2728 fn promise_rejection_value(
2729 &mut self,
2730 failure: EvalFailure,
2731 ) -> Result<(Value, ThrowOrigin), EvalFailure> {
2732 match failure {
2733 EvalFailure::ThrowValue(value) => Ok((value, ThrowOrigin::Bytecode)),
2734 EvalFailure::ThrowValueOrigin { value, origin } => Ok((value, origin)),
2735 EvalFailure::Throw(ThrowOrigin::Bytecode) => {
2736 Ok((Value::UNDEFINED, ThrowOrigin::Bytecode))
2737 }
2738 EvalFailure::Throw(origin) => {
2739 let (name, message) = match origin {
2740 ThrowOrigin::TypeError { operation } => ("TypeError", operation),
2741 ThrowOrigin::RangeError { operation } => ("RangeError", operation),
2742 ThrowOrigin::ReferenceError { operation } => ("ReferenceError", operation),
2743 ThrowOrigin::UriError { operation } => ("URIError", operation),
2744 ThrowOrigin::Bytecode => unreachable!("handled above"),
2745 };
2746 let id = self
2747 .intrinsics
2748 .builtins
2749 .id_named(name)
2750 .expect("error constructor is installed");
2751 match self.throw_error(id, message.to_owned()) {
2752 EvalFailure::ThrowValue(value) => Ok((value, origin)),
2753 EvalFailure::Runtime(kind) => Err(EvalFailure::Runtime(kind)),
2754 _ => unreachable!("error materialization returns a thrown value"),
2755 }
2756 }
2757 EvalFailure::Runtime(kind) => Err(EvalFailure::Runtime(kind)),
2758 }
2759 }
2760
2761 fn program(&self) -> &Program<Verified> {
2762 self.program
2763 .expect("module registry operations require a whole program")
2764 }
2765
2766 fn module_code(&self, module: ModuleId) -> &Module<Verified> {
2767 let index = module.get() as usize;
2768 if index >= self.dynamic_base {
2769 return &self.dynamic[index - self.dynamic_base].program.modules()[0].code;
2770 }
2771 match self.program {
2772 Some(program) => {
2773 &program
2774 .module(module)
2775 .expect("verified module id remains in bounds")
2776 .code
2777 }
2778 None => self.module,
2779 }
2780 }
2781
2782 fn program_module(&self, module: ModuleId) -> &ProgramModule<Verified> {
2783 let index = module.get() as usize;
2784 if index >= self.dynamic_base {
2785 return &self.dynamic[index - self.dynamic_base].program.modules()[0];
2786 }
2787 self.program
2788 .and_then(|program| program.module(module))
2789 .expect("verified module id remains in bounds")
2790 }
2791
2792 fn validate_dynamic_script(program: &Program<Verified>) -> Result<(), &'static str> {
2794 if program.modules().len() != 1 {
2795 return Err("script program must contain exactly one module");
2796 }
2797 if program.entry() != ModuleId::new(0) {
2798 return Err("script program entry must be module zero");
2799 }
2800 let module = &program.modules()[0];
2801 if !module.edges.is_empty() || !module.bindings.is_empty() || !module.exports.is_empty() {
2802 return Err("script program must not contain linkage metadata");
2803 }
2804 if module
2805 .code
2806 .functions()
2807 .iter()
2808 .flat_map(|function| function.code())
2809 .any(|instruction| {
2810 matches!(
2811 instruction,
2812 Instruction::Import { .. } | Instruction::Export { .. }
2813 )
2814 })
2815 {
2816 return Err("script program must not contain import or export instructions");
2817 }
2818 Ok(())
2819 }
2820
2821 fn script_heap_cost(program: &Program<Verified>) -> usize {
2822 const MODULE_BYTES: usize = 64;
2823 const FUNCTION_BYTES: usize = 32;
2824 program.modules().iter().fold(0usize, |total, module| {
2825 let constant_bytes = module
2826 .code
2827 .constants()
2828 .iter()
2829 .fold(0usize, |bytes, constant| {
2830 let payload = match constant {
2831 Constant::String(text) => text.len_units().saturating_mul(2),
2832 Constant::BigInt(value) => value.as_str().len(),
2833 Constant::Number(_)
2834 | Constant::Int32(_)
2835 | Constant::Boolean(_)
2836 | Constant::Null
2837 | Constant::Undefined => 0,
2838 };
2839 bytes
2840 .saturating_add(std::mem::size_of::<Constant>())
2841 .saturating_add(payload)
2842 });
2843 let function_bytes =
2844 module
2845 .code
2846 .functions()
2847 .iter()
2848 .fold(0usize, |bytes, function| {
2849 bytes
2850 .saturating_add(FUNCTION_BYTES)
2851 .saturating_add(
2852 function
2853 .code()
2854 .len()
2855 .saturating_mul(std::mem::size_of::<Instruction>()),
2856 )
2857 .saturating_add(function.handlers().len().saturating_mul(
2858 std::mem::size_of::<bamts_bytecode::ExceptionHandler>(),
2859 ))
2860 });
2861 total
2862 .saturating_add(MODULE_BYTES)
2863 .saturating_add(constant_bytes)
2864 .saturating_add(function_bytes)
2865 .saturating_add(module.code.verification_bytes())
2866 })
2867 }
2868
2869 fn install_script_reserving(
2870 &mut self,
2871 program: Arc<Program<Verified>>,
2872 reserved_slots: usize,
2873 reserved_bytes: usize,
2874 ) -> Result<ModuleId, RuntimeErrorKind> {
2875 Self::validate_dynamic_script(&program)
2876 .map_err(|reason| RuntimeErrorKind::InvalidDynamicScript { reason })?;
2877 if self.dynamic.len() >= self.limits.max_dynamic_modules {
2878 return Err(RuntimeErrorKind::DynamicModuleLimitExceeded {
2879 limit: self.limits.max_dynamic_modules,
2880 });
2881 }
2882 let bytes = Self::script_heap_cost(&program);
2883 let retained_bytes =
2884 bytes
2885 .checked_add(reserved_bytes)
2886 .ok_or(RuntimeErrorKind::HeapByteLimitExceeded {
2887 limit: self.limits.max_heap_bytes,
2888 })?;
2889 self.ensure_allocation_capacity(reserved_slots, retained_bytes)?;
2890 self.charge_heap(bytes)?;
2891 let index = self.dynamic_base.checked_add(self.dynamic.len()).ok_or(
2892 RuntimeErrorKind::DynamicModuleLimitExceeded {
2893 limit: self.limits.max_dynamic_modules,
2894 },
2895 )?;
2896 let module = ModuleId::new(u32::try_from(index).map_err(|_| {
2897 RuntimeErrorKind::DynamicModuleLimitExceeded {
2898 limit: self.limits.max_dynamic_modules,
2899 }
2900 })?);
2901 self.dynamic.push(DynamicModule { program, bytes });
2902 self.registry.modules.push(ModuleInstance {
2903 binding_cells: Vec::new(),
2904 constant_cells: Vec::new(),
2905 namespace: None,
2906 state: ModuleState::Unevaluated,
2907 });
2908 debug_assert_eq!(
2909 self.dynamic
2910 .last()
2911 .expect("installed script remains retained")
2912 .bytes,
2913 bytes
2914 );
2915 debug_assert_eq!(
2916 self.registry.modules.len(),
2917 self.dynamic_base + self.dynamic.len()
2918 );
2919 Ok(module)
2920 }
2921
2922 fn allocate_cell(&mut self, value: Value, module: ModuleId) -> Result<CellId, RuntimeError> {
2923 if self.registry.cells.len() >= self.limits.max_module_cells {
2924 return Err(self.program_error(
2925 module,
2926 RuntimeErrorKind::ModuleCellLimitExceeded {
2927 limit: self.limits.max_module_cells,
2928 },
2929 ));
2930 }
2931 let id = CellId(self.registry.cells.len());
2932 self.registry.cells.push(Cell { value });
2933 Ok(id)
2934 }
2935
2936 pub(crate) fn instantiate_modules(&mut self) -> Result<(), RuntimeError> {
2937 debug_assert!(
2938 self.dynamic.is_empty(),
2939 "module instantiation precedes dynamic script installation"
2940 );
2941 let program = self
2942 .program
2943 .expect("module registry operations require a whole program");
2944 self.registry.modules = program
2945 .modules()
2946 .iter()
2947 .map(|module| ModuleInstance {
2948 binding_cells: vec![None; module.bindings.len()],
2949 constant_cells: vec![None; module.code.constants().len()],
2950 namespace: None,
2951 state: ModuleState::Unevaluated,
2952 })
2953 .collect();
2954
2955 for module_index in 0..program.modules().len() {
2956 let module_id = ModuleId::new(module_index as u32);
2957 let bindings = program.modules()[module_index].bindings.clone();
2958 for (binding_index, binding) in bindings.into_iter().enumerate() {
2959 let initial = match binding.kind {
2960 BindingKind::Hoisted => Some(Value::UNDEFINED),
2961 BindingKind::Lexical => Some(Value::UNINITIALIZED),
2962 BindingKind::Imported { .. } | BindingKind::Namespace { .. } => None,
2963 };
2964 if let Some(value) = initial {
2965 let cell = self.allocate_cell(value, module_id)?;
2966 self.registry.modules[module_index].binding_cells[binding_index] = Some(cell);
2967 }
2968 }
2969 }
2970
2971 for module_index in 0..program.modules().len() {
2972 let module_id = ModuleId::new(module_index as u32);
2973 let bindings = program.modules()[module_index].bindings.clone();
2974 for (binding_index, binding) in bindings.into_iter().enumerate() {
2975 let cell = match binding.kind {
2976 BindingKind::Hoisted | BindingKind::Lexical => continue,
2977 BindingKind::Imported { edge, name } => {
2978 let dependency = program.modules()[module_index].edges[edge.get() as usize];
2979 match dependency.target {
2980 EdgeTarget::External => {
2981 let name = self.constant_text(module_id, name).clone();
2982 self.external_export_cell(module_id, edge, &name)?
2983 }
2984 EdgeTarget::Local(target) => match program
2985 .resolve_export(target, self.constant_text(module_id, name))
2986 {
2987 Some(ResolvedExport::Local { module, binding }) => {
2988 self.registry.modules[module.get() as usize].binding_cells
2989 [binding.get() as usize]
2990 .expect("own cells are allocated before aliases link")
2991 }
2992 Some(ResolvedExport::External { module, edge, name }) => {
2993 let name = self.constant_text(module, name).clone();
2994 self.external_export_cell(module, edge, &name)?
2995 }
2996 None => {
2997 return Err(self.program_error(
2998 module_id,
2999 RuntimeErrorKind::InvalidVerifiedProgram {
3000 module: module_id,
3001 instruction: Instruction::Import {
3002 dst: bamts_bytecode::Register::new(0),
3003 specifier: name,
3004 },
3005 },
3006 ));
3007 }
3008 },
3009 }
3010 }
3011 BindingKind::Namespace { edge } => {
3012 let dependency = program.modules()[module_index].edges[edge.get() as usize];
3013 let namespace = match dependency.target {
3014 EdgeTarget::Local(target) => {
3015 self.module_namespace(target, module_id)?
3016 }
3017 EdgeTarget::External => self.external_namespace(module_id, edge)?,
3018 };
3019 self.allocate_cell(namespace, module_id)?
3020 }
3021 };
3022 self.registry.modules[module_index].binding_cells[binding_index] = Some(cell);
3023 }
3024 }
3025
3026 for module_index in 0..program.modules().len() {
3027 let bindings = &program.modules()[module_index].bindings;
3028 let constants = program.modules()[module_index].code.constants();
3029 for (constant_index, constant) in constants.iter().enumerate() {
3030 let Constant::String(name) = constant else {
3031 continue;
3032 };
3033 if let Some((binding_index, _)) =
3034 bindings.iter().enumerate().find(|(_, binding)| {
3035 self.constant_text(ModuleId::new(module_index as u32), binding.name) == name
3036 })
3037 {
3038 self.registry.modules[module_index].constant_cells[constant_index] =
3039 self.registry.modules[module_index].binding_cells[binding_index];
3040 }
3041 }
3042 }
3043 Ok(())
3044 }
3045
3046 fn module_namespace(
3047 &mut self,
3048 target: ModuleId,
3049 requester: ModuleId,
3050 ) -> Result<Value, RuntimeError> {
3051 if let Some(value) = self.registry.modules[target.get() as usize].namespace {
3052 return Ok(value);
3053 }
3054 let exported_names: Vec<EcmaString> = self
3055 .program_module(target)
3056 .exports
3057 .iter()
3058 .map(|export| self.constant_text(target, export.name).clone())
3059 .collect();
3060 for exported_name in exported_names {
3061 if let Some(ResolvedExport::External { module, edge, name }) =
3062 self.program().resolve_export(target, &exported_name)
3063 {
3064 let name = self.constant_text(module, name).clone();
3065 self.external_export_cell(module, edge, &name)?;
3066 }
3067 }
3068 let value = self
3069 .allocate(HeapEntry::ModuleNamespace { module: target })
3070 .map_err(|kind| self.program_error(requester, kind))?;
3071 self.registry.modules[target.get() as usize].namespace = Some(value);
3072 Ok(value)
3073 }
3074
3075 fn external_specifier(&self, module: ModuleId, edge: EdgeId) -> Option<EcmaString> {
3076 let dependency = self.program_module(module).edges[edge.get() as usize];
3077 let specifier = self.constant_text(module, dependency.specifier);
3078 self.registry
3079 .external
3080 .contains_key(specifier)
3081 .then(|| specifier.clone())
3082 }
3083
3084 fn external_namespace(
3085 &mut self,
3086 module: ModuleId,
3087 edge: EdgeId,
3088 ) -> Result<Value, RuntimeError> {
3089 let Some(specifier) = self.external_specifier(module, edge) else {
3090 return Err(self.program_error(
3091 module,
3092 RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3093 ));
3094 };
3095 let export_names: Vec<EcmaString> = self.registry.external[&specifier]
3096 .exports
3097 .keys()
3098 .cloned()
3099 .collect();
3100 for name in export_names {
3101 self.external_export_cell(module, edge, &name)?;
3102 }
3103 Ok(self.registry.external[&specifier].namespace)
3104 }
3105
3106 fn external_export_cell(
3107 &mut self,
3108 module: ModuleId,
3109 edge: EdgeId,
3110 name: &EcmaString,
3111 ) -> Result<CellId, RuntimeError> {
3112 let Some(specifier) = self.external_specifier(module, edge) else {
3113 return Err(self.program_error(
3114 module,
3115 RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3116 ));
3117 };
3118 let Some(export) = self.registry.external[&specifier]
3119 .exports
3120 .get(name)
3121 .copied()
3122 else {
3123 return Err(self.program_error(
3124 module,
3125 RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3126 ));
3127 };
3128 if let Some(cell) = export.cell {
3129 return Ok(cell);
3130 }
3131 let cell = self.allocate_cell(export.value, module)?;
3132 self.registry
3133 .external
3134 .get_mut(&specifier)
3135 .expect("external module remains registered")
3136 .exports
3137 .get_mut(name)
3138 .expect("external export remains registered")
3139 .cell = Some(cell);
3140 Ok(cell)
3141 }
3142
3143 pub(crate) fn resolve_import(
3144 &self,
3145 module: ModuleId,
3146 specifier: ConstantId,
3147 ) -> Result<ImportTarget, RuntimeErrorKind> {
3148 let name = self.constant_text(module, specifier);
3149 self.program_module(module)
3150 .edges
3151 .iter()
3152 .enumerate()
3153 .find(|(_, edge)| {
3154 edge.kind.has_dynamic() && self.constant_text(module, edge.specifier) == name
3155 })
3156 .map(|(index, edge)| match edge.target {
3157 EdgeTarget::Local(target) => ImportTarget::Local(target),
3158 EdgeTarget::External => ImportTarget::External(EdgeId::new(index as u32)),
3159 })
3160 .ok_or(RuntimeErrorKind::DynamicImportEdgeMissing { module, specifier })
3161 }
3162
3163 pub(crate) fn imported_namespace(
3164 &mut self,
3165 requester: ModuleId,
3166 target: ImportTarget,
3167 ) -> Result<Value, RuntimeErrorKind> {
3168 match target {
3169 ImportTarget::Local(target) => self.module_namespace(target, requester),
3170 ImportTarget::External(edge) => self.external_namespace(requester, edge),
3171 }
3172 .map_err(|error| error.kind)
3173 }
3174
3175 fn run_import_entry(&mut self, module: ModuleId) -> Result<(), RuntimeError> {
3176 let function = self.module_code(module).entry();
3177 let stop_depth = self.frames.len();
3178 self.push_frame(
3179 RuntimeFunction { module, function },
3180 &[],
3181 Value::UNDEFINED,
3182 Value::UNDEFINED,
3183 &[],
3184 None,
3185 )?;
3186 let result = self.run_loop(stop_depth).and_then(|execution| {
3187 execution.map(|_| ()).ok_or_else(|| {
3188 self.program_error(
3189 module,
3190 RuntimeErrorKind::InvalidVerifiedProgram {
3191 module,
3192 instruction: Instruction::Halt,
3193 },
3194 )
3195 })
3196 });
3197 if result.is_err() {
3198 self.unwind_frames_to(stop_depth);
3199 }
3200 result
3201 }
3202
3203 fn evaluate_import(&mut self, module: ModuleId) -> Result<(), RuntimeError> {
3204 let dependencies = match self.begin_module_evaluation(module)? {
3205 ModuleEvaluation::Cycle => return Ok(()),
3206 ModuleEvaluation::Evaluated(result) => return result,
3207 ModuleEvaluation::Ready(dependencies) => dependencies,
3208 };
3209 for dependency in dependencies {
3210 if let Err(error) = self.evaluate_import(dependency) {
3211 self.settle_module_evaluation(module, Err(error.clone()));
3212 return Err(error);
3213 }
3214 }
3215 let result = self.run_import_entry(module);
3216 self.settle_module_evaluation(module, result.clone());
3217 result
3218 }
3219
3220 fn import_namespace(
3221 &mut self,
3222 requester: ModuleId,
3223 specifier: ConstantId,
3224 ) -> Result<Value, EvalFailure> {
3225 let target = self
3226 .resolve_import(requester, specifier)
3227 .map_err(EvalFailure::Runtime)?;
3228 if let ImportTarget::Local(module) = target {
3229 self.evaluate_import(module)
3230 .map_err(|error| import_failure(&error))?;
3231 }
3232 self.imported_namespace(requester, target)
3233 .map_err(EvalFailure::Runtime)
3234 }
3235 fn evaluate_module(&mut self, module: ModuleId) -> Result<Option<Execution>, RuntimeError> {
3236 let dependencies = match self.begin_module_evaluation(module)? {
3237 ModuleEvaluation::Cycle => return Ok(None),
3238 ModuleEvaluation::Evaluated(result) => return result.map(|()| None),
3239 ModuleEvaluation::Ready(dependencies) => dependencies,
3240 };
3241 for dependency in dependencies {
3242 if let Err(error) = self.evaluate_module(dependency) {
3243 return self.finish_module_evaluation(module, Err(error)).map(Some);
3244 }
3245 }
3246
3247 let code = self.module_code(module);
3248 let function = code.entry().get() as usize;
3249 let metadata = &code.functions()[function];
3250 let register_count = metadata.register_count() as usize;
3251 let result = if self.limits.max_call_depth < 1 {
3252 Err(self.program_error(
3253 module,
3254 RuntimeErrorKind::CallDepthExceeded {
3255 limit: self.limits.max_call_depth,
3256 },
3257 ))
3258 } else if register_count > self.limits.max_total_registers {
3259 Err(self.program_error(
3260 module,
3261 RuntimeErrorKind::RegisterLimitExceeded {
3262 limit: self.limits.max_total_registers,
3263 },
3264 ))
3265 } else {
3266 self.frames.push(Frame::new(
3267 RuntimeFunction {
3268 module,
3269 function: FunctionId::new(function as u32),
3270 },
3271 metadata,
3272 &[],
3273 Value::UNDEFINED,
3274 Value::UNDEFINED,
3275 &[],
3276 None,
3277 ));
3278 self.live_registers = register_count;
3279 self.run_loop(0).and_then(|execution| {
3280 execution.ok_or_else(|| {
3281 self.program_error(
3282 module,
3283 RuntimeErrorKind::InvalidVerifiedProgram {
3284 module,
3285 instruction: Instruction::Halt,
3286 },
3287 )
3288 })
3289 })
3290 };
3291 self.finish_module_evaluation(module, result).map(Some)
3292 }
3293
3294 pub(crate) fn begin_module_evaluation(
3295 &mut self,
3296 module: ModuleId,
3297 ) -> Result<ModuleEvaluation, RuntimeError> {
3298 match self.registry.modules[module.get() as usize].state.clone() {
3299 ModuleState::Evaluating => return Ok(ModuleEvaluation::Cycle),
3300 ModuleState::Evaluated(result) => return Ok(ModuleEvaluation::Evaluated(result)),
3301 ModuleState::Unevaluated => {}
3302 }
3303 self.registry.modules[module.get() as usize].state = ModuleState::Evaluating;
3304
3305 let mut dependencies = Vec::new();
3306 for (edge_index, edge) in self
3307 .program_module(module)
3308 .edges
3309 .iter()
3310 .copied()
3311 .enumerate()
3312 {
3313 if !edge.kind.has_static() {
3314 continue;
3315 }
3316 match edge.target {
3317 EdgeTarget::Local(dependency) => dependencies.push(dependency),
3318 EdgeTarget::External
3319 if self
3320 .external_specifier(module, EdgeId::new(edge_index as u32))
3321 .is_some() => {}
3322 EdgeTarget::External => {
3323 let error = self.program_error(
3324 module,
3325 RuntimeErrorKind::ExternalModuleUnavailable {
3326 module,
3327 edge: EdgeId::new(edge_index as u32),
3328 },
3329 );
3330 self.settle_module_evaluation(module, Err(error.clone()));
3331 return Err(error);
3332 }
3333 }
3334 }
3335 Ok(ModuleEvaluation::Ready(dependencies))
3336 }
3337
3338 pub(crate) fn finish_module_evaluation(
3339 &mut self,
3340 module: ModuleId,
3341 result: Result<Execution, RuntimeError>,
3342 ) -> Result<Execution, RuntimeError> {
3343 if result.is_err() {
3344 self.frames.clear();
3345 self.live_registers = 0;
3346 }
3347 let stored = result.as_ref().map(|_| ()).map_err(Clone::clone);
3348 self.settle_module_evaluation(module, stored);
3349 result
3350 }
3351
3352 pub(crate) fn settle_module_evaluation(
3353 &mut self,
3354 module: ModuleId,
3355 result: Result<(), RuntimeError>,
3356 ) {
3357 match result {
3358 Ok(()) => {
3359 self.registry.modules[module.get() as usize].state = ModuleState::Evaluated(Ok(()));
3360 }
3361 Err(error) if matches!(error.kind, RuntimeErrorKind::UncaughtThrow { .. }) => {
3362 self.registry.modules[module.get() as usize].state =
3363 ModuleState::Evaluated(Err(error));
3364 }
3365 Err(_) => self.abort_module_evaluation(module),
3366 }
3367 }
3368
3369 pub(crate) fn abort_module_evaluation(&mut self, module: ModuleId) {
3370 if matches!(
3371 self.registry.modules[module.get() as usize].state,
3372 ModuleState::Evaluating
3373 ) {
3374 self.registry.modules[module.get() as usize].state = ModuleState::Unevaluated;
3375 }
3376 }
3377
3378 pub(crate) fn constant_text(&self, module: ModuleId, id: ConstantId) -> &EcmaString {
3379 match &self.module_code(module).constants()[id.get() as usize] {
3380 Constant::String(text) => text,
3381 _ => unreachable!("verified module names are strings"),
3382 }
3383 }
3384
3385 fn program_error(&self, module: ModuleId, kind: RuntimeErrorKind) -> RuntimeError {
3386 let code = self.module_code(module);
3387 let function = code.entry().get() as usize;
3388 let instruction = code.functions()[function]
3389 .code()
3390 .first()
3391 .copied()
3392 .unwrap_or(Instruction::Halt);
3393 RuntimeError {
3394 kind,
3395 function: FunctionId::new(function as u32),
3396 pc: Pc::new(0),
3397 source: RuntimeSource {
3398 function_name: None,
3399 instruction,
3400 },
3401 }
3402 }
3403
3404 fn run_loop(&mut self, stop_depth: usize) -> Result<Option<Execution>, RuntimeError> {
3405 if self.frames.len().saturating_add(self.native_depth) > self.limits.max_call_depth {
3406 return Err(self.error_here(RuntimeErrorKind::CallDepthExceeded {
3407 limit: self.limits.max_call_depth,
3408 }));
3409 }
3410 if self.live_registers > self.limits.max_total_registers {
3411 return Err(self.error_here(RuntimeErrorKind::RegisterLimitExceeded {
3412 limit: self.limits.max_total_registers,
3413 }));
3414 }
3415
3416 loop {
3417 let frame_index = self.frames.len() - 1;
3418 let (module_id, function_index, pc) = {
3419 let frame = &self.frames[frame_index];
3420 (frame.module, frame.function, frame.pc)
3421 };
3422 if let Err(kind) = self.consume_fuel(1) {
3423 return Err(self.error_at(kind, function_index, pc));
3424 }
3425 let instruction = self.module_code(module_id).functions()[function_index].code()[pc];
3426
3427 match instruction {
3428 Instruction::LoadConst { dst, constant } => {
3429 let value = self.load_constant(constant, function_index, pc)?;
3430 self.write_register(frame_index, dst.get(), value);
3431 self.frames[frame_index].pc = pc + 1;
3432 }
3433 Instruction::Move { dst, src } => {
3434 let value = self.read_register(frame_index, src.get());
3435 self.write_register(frame_index, dst.get(), value);
3436 self.frames[frame_index].pc = pc + 1;
3437 }
3438 Instruction::Unary { dst, op, operand } => {
3439 let value = self.read_register(frame_index, operand.get());
3440 match self.eval_unary(op, value) {
3441 Ok(result) => {
3442 self.write_register(frame_index, dst.get(), result);
3443 self.frames[frame_index].pc = pc + 1;
3444 }
3445 Err(failure) => self.resolve_failure(failure, pc)?,
3446 }
3447 }
3448 Instruction::Binary {
3449 dst,
3450 op,
3451 left,
3452 right,
3453 } => {
3454 let left = self.read_register(frame_index, left.get());
3455 let right = self.read_register(frame_index, right.get());
3456 match self.eval_binary(op, left, right) {
3457 Ok(result) => {
3458 self.write_register(frame_index, dst.get(), result);
3459 self.frames[frame_index].pc = pc + 1;
3460 }
3461 Err(failure) => self.resolve_failure(failure, pc)?,
3462 }
3463 }
3464 Instruction::CreateObject { dst } => {
3465 let value = self
3466 .allocate(HeapEntry::Object {
3467 properties: PropertyMap::default(),
3468 prototype: Some(self.intrinsics.object_prototype),
3469 boxed_primitive: None,
3470 extensible: true,
3471 })
3472 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3473 self.write_register(frame_index, dst.get(), value);
3474 self.frames[frame_index].pc = pc + 1;
3475 }
3476 Instruction::CreateArray { dst } => {
3477 let value = self
3478 .allocate(HeapEntry::Array {
3479 elements: Vec::new(),
3480 properties: PropertyMap::default(),
3481 prototype: Some(self.intrinsics.array_prototype),
3482 extensible: true,
3483 length_writable: true,
3484 })
3485 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3486 self.write_register(frame_index, dst.get(), value);
3487 self.frames[frame_index].pc = pc + 1;
3488 }
3489 Instruction::CreateCell { dst } => {
3490 let value = self
3491 .allocate(HeapEntry::Array {
3492 elements: vec![Value::UNINITIALIZED],
3493 properties: PropertyMap::default(),
3494 prototype: Some(self.intrinsics.array_prototype),
3495 extensible: true,
3496 length_writable: true,
3497 })
3498 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3499 self.write_register(frame_index, dst.get(), value);
3500 self.frames[frame_index].pc = pc + 1;
3501 }
3502 Instruction::CreateClosure {
3503 dst,
3504 function,
3505 captures,
3506 } => match self.read_captures(frame_index, captures.get(), function) {
3507 Ok(captures) => {
3508 let value = self
3509 .allocate(HeapEntry::Function {
3510 module: module_id,
3511 function,
3512 captures,
3513 properties: PropertyMap::default(),
3514 prototype: Some(self.intrinsics.function_prototype),
3515 extensible: true,
3516 })
3517 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3518 self.write_register(frame_index, dst.get(), value);
3519 self.frames[frame_index].pc = pc + 1;
3520 }
3521 Err(failure) => self.resolve_failure(failure, pc)?,
3522 },
3523 Instruction::GetProperty { dst, object, key } => {
3524 let object = self.read_register(frame_index, object.get());
3525 let key_value = self.read_register(frame_index, key.get());
3526 let key = match self.to_property_key(key_value) {
3527 Ok(key) => key,
3528 Err(failure) => {
3529 self.resolve_failure(failure, pc)?;
3530 continue;
3531 }
3532 };
3533 match self.resolve_get(object, &key) {
3534 Ok(GetOutcome::Value(value)) => {
3535 self.write_register(frame_index, dst.get(), value);
3536 self.frames[frame_index].pc = pc + 1;
3537 }
3538 Ok(GetOutcome::Text(text)) => {
3539 let value = self
3540 .allocate(HeapEntry::String(text))
3541 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3542 self.write_register(frame_index, dst.get(), value);
3543 self.frames[frame_index].pc = pc + 1;
3544 }
3545 Ok(GetOutcome::Getter(getter)) => {
3546 self.frames[frame_index].pc = pc + 1;
3547 self.execute_call(CallRequest {
3548 callee: getter,
3549 this_value: object,
3550 arguments: &[],
3551 destination: Some(dst.get()),
3552 call_pc: pc,
3553 constructed: None,
3554 new_target: Value::UNDEFINED,
3555 })?;
3556 }
3557 Err(failure) => self.resolve_failure(failure, pc)?,
3558 }
3559 }
3560 Instruction::SetProperty { object, key, value } => {
3561 let object = self.read_register(frame_index, object.get());
3562 let value = self.read_register(frame_index, value.get());
3563 let key_value = self.read_register(frame_index, key.get());
3564 let key = match self.to_property_key(key_value) {
3565 Ok(key) => key,
3566 Err(failure) => {
3567 self.resolve_failure(failure, pc)?;
3568 continue;
3569 }
3570 };
3571 match self.resolve_set(object, key, value) {
3572 Ok(SetOutcome::Done) => self.frames[frame_index].pc = pc + 1,
3573 Ok(SetOutcome::Setter(setter)) => {
3574 self.frames[frame_index].pc = pc + 1;
3575 self.execute_call(CallRequest {
3576 callee: setter,
3577 this_value: object,
3578 arguments: &[value],
3579 destination: None,
3580 call_pc: pc,
3581 constructed: None,
3582 new_target: Value::UNDEFINED,
3583 })?;
3584 }
3585 Err(failure) => self.resolve_failure(failure, pc)?,
3586 }
3587 }
3588 Instruction::DeleteProperty { dst, object, key } => {
3589 let object = self.read_register(frame_index, object.get());
3590 let key_value = self.read_register(frame_index, key.get());
3591 let key = match self.to_property_key(key_value) {
3592 Ok(key) => key,
3593 Err(failure) => {
3594 self.resolve_failure(failure, pc)?;
3595 continue;
3596 }
3597 };
3598 match self.delete_property(object, &key) {
3599 Ok(deleted) => {
3600 self.write_register(frame_index, dst.get(), Value::boolean(deleted));
3601 self.frames[frame_index].pc = pc + 1;
3602 }
3603 Err(failure) => self.resolve_failure(failure, pc)?,
3604 }
3605 }
3606 Instruction::DefineAccessor {
3607 object,
3608 key,
3609 accessor,
3610 kind,
3611 } => {
3612 let object = self.read_register(frame_index, object.get());
3613 let accessor = self.read_register(frame_index, accessor.get());
3614 let key_value = self.read_register(frame_index, key.get());
3615 let key = match self.to_property_key(key_value) {
3616 Ok(key) => key,
3617 Err(failure) => {
3618 self.resolve_failure(failure, pc)?;
3619 continue;
3620 }
3621 };
3622 match self.define_accessor(object, key, accessor, kind) {
3623 Ok(()) => self.frames[frame_index].pc = pc + 1,
3624 Err(failure) => self.resolve_failure(failure, pc)?,
3625 }
3626 }
3627 Instruction::Call {
3628 dst,
3629 callee,
3630 this_value,
3631 arguments,
3632 } => {
3633 let callee = self.read_register(frame_index, callee.get());
3634 let this_value = self.read_register(frame_index, this_value.get());
3635 match self.read_arguments(frame_index, arguments.get()) {
3636 Ok(arguments) => {
3637 self.frames[frame_index].pc = pc + 1;
3638 self.execute_call(CallRequest {
3639 callee,
3640 this_value,
3641 arguments: &arguments,
3642 destination: Some(dst.get()),
3643 call_pc: pc,
3644 constructed: None,
3645 new_target: Value::UNDEFINED,
3646 })?;
3647 }
3648 Err(failure) => self.resolve_failure(failure, pc)?,
3649 }
3650 }
3651 Instruction::Construct {
3652 dst,
3653 callee,
3654 arguments,
3655 } => {
3656 let callee = self.read_register(frame_index, callee.get());
3657 match self.read_arguments(frame_index, arguments.get()) {
3658 Ok(arguments) => {
3659 self.frames[frame_index].pc = pc + 1;
3660 self.execute_construct(callee, &arguments, dst.get(), pc)?;
3661 }
3662 Err(failure) => self.resolve_failure(failure, pc)?,
3663 }
3664 }
3665 Instruction::LoadGlobal { dst, name } => match self.load_global(module_id, name) {
3666 Ok(Some(value)) => {
3667 self.write_register(frame_index, dst.get(), value);
3668 self.frames[frame_index].pc = pc + 1;
3669 }
3670 Ok(None) => self.throw(
3671 Value::UNDEFINED,
3672 ThrowOrigin::ReferenceError {
3673 operation: "global is not defined",
3674 },
3675 pc,
3676 )?,
3677 Err(kind) => return Err(self.error_here_at(kind, pc)),
3678 },
3679 Instruction::StoreGlobal { name, value } => {
3680 let value = self.read_register(frame_index, value.get());
3681 match self.store_global(module_id, name, value) {
3682 Ok(()) => self.frames[frame_index].pc = pc + 1,
3683 Err(failure) => self.resolve_failure(failure, pc)?,
3684 }
3685 }
3686 Instruction::TypeOfGlobal { dst, name } => {
3687 let text = match self.load_global(module_id, name) {
3688 Ok(value) => value.map_or("undefined", |value| self.type_of(value)),
3689 Err(kind) => return Err(self.error_here_at(kind, pc)),
3690 };
3691 let value = self
3692 .allocate(HeapEntry::String(EcmaString::from_utf8(text)))
3693 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3694 self.write_register(frame_index, dst.get(), value);
3695 self.frames[frame_index].pc = pc + 1;
3696 }
3697 Instruction::LoadThis { dst } => {
3698 let value = self.frames[frame_index].this_value;
3699 self.write_register(frame_index, dst.get(), value);
3700 self.frames[frame_index].pc = pc + 1;
3701 }
3702 Instruction::LoadArguments { dst } => {
3703 let value = self.materialize_arguments(frame_index, function_index, pc)?;
3704 self.write_register(frame_index, dst.get(), value);
3705 self.frames[frame_index].pc = pc + 1;
3706 }
3707 Instruction::LoadNewTarget { dst } => {
3708 let value = self.frames[frame_index].new_target;
3709 self.write_register(frame_index, dst.get(), value);
3710 self.frames[frame_index].pc = pc + 1;
3711 }
3712 Instruction::ArrayPush { array, value } => {
3713 let array = self.read_register(frame_index, array.get());
3714 let value = self.read_register(frame_index, value.get());
3715 match self.array_push(array, value) {
3716 Ok(()) => self.frames[frame_index].pc = pc + 1,
3717 Err(failure) => self.resolve_failure(failure, pc)?,
3718 }
3719 }
3720 Instruction::ArrayExtend { array, iterable } => {
3721 let array = self.read_register(frame_index, array.get());
3722 let iterable = self.read_register(frame_index, iterable.get());
3723 match self.array_extend(array, iterable) {
3724 Ok(()) => self.frames[frame_index].pc = pc + 1,
3725 Err(failure) => self.resolve_failure(failure, pc)?,
3726 }
3727 }
3728 Instruction::ObjectSpread { target, source } => {
3729 let target = self.read_register(frame_index, target.get());
3730 let source = self.read_register(frame_index, source.get());
3731 match self.object_spread(target, source) {
3732 Ok(()) => self.frames[frame_index].pc = pc + 1,
3733 Err(failure) => self.resolve_failure(failure, pc)?,
3734 }
3735 }
3736 Instruction::SetPrototype { object, prototype } => {
3737 let object = self.read_register(frame_index, object.get());
3738 let prototype = self.read_register(frame_index, prototype.get());
3739 match self.set_prototype(object, prototype) {
3740 Ok(()) => self.frames[frame_index].pc = pc + 1,
3741 Err(failure) => self.resolve_failure(failure, pc)?,
3742 }
3743 }
3744 Instruction::CreatePrivateName { dst, description } => {
3745 let description = self.constant_string(description).clone();
3746 let value = self
3747 .allocate(HeapEntry::PrivateName { description })
3748 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3749 self.write_register(frame_index, dst.get(), value);
3750 self.frames[frame_index].pc = pc + 1;
3751 }
3752 Instruction::CreateRegExp {
3753 dst,
3754 pattern,
3755 flags,
3756 } => {
3757 let pattern = self.constant_string(pattern).clone();
3758 let flags = self.constant_string(flags).clone();
3759 let value = self
3760 .allocate(HeapEntry::RegExp {
3761 pattern,
3762 flags,
3763 properties: PropertyMap::default(),
3764 prototype: Some(self.intrinsics.regexp_prototype()),
3765 extensible: true,
3766 })
3767 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3768 self.write_register(frame_index, dst.get(), value);
3769 self.frames[frame_index].pc = pc + 1;
3770 }
3771 Instruction::GetIterator { dst, src, kind } => {
3772 let src = self.read_register(frame_index, src.get());
3773 match self.create_iterator(src, kind) {
3774 Ok(value) => {
3775 self.write_register(frame_index, dst.get(), value);
3776 self.frames[frame_index].pc = pc + 1;
3777 }
3778 Err(failure) => self.resolve_failure(failure, pc)?,
3779 }
3780 }
3781 Instruction::IteratorNext {
3782 done,
3783 value,
3784 iterator,
3785 } => {
3786 let iterator = self.read_register(frame_index, iterator.get());
3787 match self.iterator_next(iterator) {
3788 Ok((is_done, produced)) => {
3789 self.write_register(frame_index, done.get(), Value::boolean(is_done));
3790 self.write_register(frame_index, value.get(), produced);
3791 self.frames[frame_index].pc = pc + 1;
3792 }
3793 Err(failure) => self.resolve_failure(failure, pc)?,
3794 }
3795 }
3796 Instruction::Jump { target } => {
3797 self.frames[frame_index].pc = target.get() as usize;
3798 }
3799 Instruction::JumpIfTrue { condition, target } => {
3800 let condition = self.read_register(frame_index, condition.get());
3801 self.frames[frame_index].pc = if self.truthy(condition) {
3802 target.get() as usize
3803 } else {
3804 pc + 1
3805 };
3806 }
3807 Instruction::JumpIfFalse { condition, target } => {
3808 let condition = self.read_register(frame_index, condition.get());
3809 self.frames[frame_index].pc = if self.truthy(condition) {
3810 pc + 1
3811 } else {
3812 target.get() as usize
3813 };
3814 }
3815 Instruction::Return { value } => {
3816 let value = self.read_register(frame_index, value.get());
3817 if let Some(execution) = self.complete_frame(value) {
3818 return Ok(Some(execution));
3819 }
3820 if self.frames.len() == stop_depth {
3821 return Ok(None);
3822 }
3823 }
3824 Instruction::Throw { value } => {
3825 let value = self.read_register(frame_index, value.get());
3826 self.throw(value, ThrowOrigin::Bytecode, pc)?;
3827 }
3828 Instruction::Suspend { src, .. }
3829 if self
3830 .async_boundaries
3831 .last()
3832 .is_some_and(|boundary| *boundary == frame_index) =>
3833 {
3834 let awaited = self.read_register(frame_index, src.get());
3835 let frame = self.frames.pop().expect("async activation is executing");
3836 self.pending_async_suspend = Some((
3837 awaited,
3838 SuspendedActivation {
3839 target: RuntimeFunction {
3840 module: frame.module,
3841 function: FunctionId::new(frame.function as u32),
3842 },
3843 registers: frame.registers,
3844 this_value: frame.this_value,
3845 new_target: frame.new_target,
3846 args: frame.args,
3847 arguments_object: frame.arguments_object,
3848 resume_token: pc as u32 + 1,
3849 },
3850 ));
3851 return Ok(None);
3852 }
3853 Instruction::Suspend { src, .. }
3854 if self
3855 .generator_boundaries
3856 .last()
3857 .is_some_and(|boundary| *boundary == frame_index) =>
3858 {
3859 let value = self.read_register(frame_index, src.get());
3860 let frame = self
3861 .frames
3862 .pop()
3863 .expect("generator activation is executing");
3864 self.pending_generator_resume = Some(GeneratorResume::Yield {
3865 value,
3866 activation: SuspendedActivation {
3867 target: RuntimeFunction {
3868 module: frame.module,
3869 function: FunctionId::new(frame.function as u32),
3870 },
3871 registers: frame.registers,
3872 this_value: frame.this_value,
3873 new_target: frame.new_target,
3874 args: frame.args,
3875 arguments_object: frame.arguments_object,
3876 resume_token: pc as u32 + 1,
3877 },
3878 });
3879 return Ok(None);
3880 }
3881 Instruction::Suspend { .. } => {
3882 self.throw_type("suspend outside an engine-owned event loop", pc)?;
3883 }
3884 Instruction::Import { dst, specifier } => {
3885 match self.import_namespace(module_id, specifier) {
3886 Ok(namespace) => {
3887 self.write_register(frame_index, dst.get(), namespace);
3888 self.frames[frame_index].pc = pc + 1;
3889 }
3890 Err(failure) => self.resolve_failure(failure, pc)?,
3891 }
3892 }
3893 Instruction::Export { .. } => {
3894 return Err(self.error_here_at(
3895 RuntimeErrorKind::InvalidVerifiedProgram {
3896 module: module_id,
3897 instruction,
3898 },
3899 pc,
3900 ));
3901 }
3902 Instruction::Halt => {
3903 if let Some(execution) = self.complete_frame(Value::UNDEFINED) {
3904 return Ok(Some(execution));
3905 }
3906 if self.frames.len() == stop_depth {
3907 return Ok(None);
3908 }
3909 }
3910 }
3911 }
3912 }
3913
3914 fn read_register(&self, frame: usize, register: u32) -> Value {
3915 self.frames[frame].registers[register as usize]
3916 }
3917
3918 fn write_register(&mut self, frame: usize, register: u32, value: Value) {
3919 self.frames[frame].registers[register as usize] = value;
3920 }
3921
3922 fn constant_string(&self, id: ConstantId) -> &EcmaString {
3923 self.constant_text(self.active_module_id(), id)
3924 }
3925
3926 fn load_constant(
3927 &mut self,
3928 id: ConstantId,
3929 function: usize,
3930 pc: usize,
3931 ) -> Result<Value, RuntimeError> {
3932 self.load_constant_value(self.active_module_id(), id)
3933 .map_err(|kind| self.error_at(kind, function, pc))
3934 }
3935
3936 fn allocate(&mut self, entry: HeapEntry) -> Result<Value, RuntimeErrorKind> {
3937 let bytes = entry.initial_bytes();
3938 self.ensure_allocation_capacity(1, bytes)?;
3939 self.heap_bytes += bytes;
3940 let slot = self.heap.len() as u32 + 1;
3941 self.heap.push(entry);
3942 let id = SlotId::from_parts(RUNTIME_HEAP_SEGMENT, slot)
3943 .expect("runtime segment and one-based slot are nonzero");
3944 Ok(Value::heap_ref(id))
3945 }
3946
3947 fn ensure_allocation_capacity(
3948 &self,
3949 additional_slots: usize,
3950 additional_bytes: usize,
3951 ) -> Result<(), RuntimeErrorKind> {
3952 let used_slots = self.heap.len().saturating_sub(self.intrinsic_slots);
3953 let slots_fit_limit = used_slots
3954 .checked_add(additional_slots)
3955 .is_some_and(|total| total <= self.limits.max_heap_slots);
3956 let slots_fit_value = self
3957 .heap
3958 .len()
3959 .checked_add(additional_slots)
3960 .is_some_and(|total| total <= u32::MAX as usize);
3961 if !slots_fit_limit || !slots_fit_value {
3962 return Err(RuntimeErrorKind::HeapSlotLimitExceeded {
3963 limit: self.limits.max_heap_slots,
3964 });
3965 }
3966 let bytes_fit = self
3967 .heap_bytes
3968 .checked_add(additional_bytes)
3969 .is_some_and(|total| total <= self.limits.max_heap_bytes);
3970 if !bytes_fit {
3971 return Err(RuntimeErrorKind::HeapByteLimitExceeded {
3972 limit: self.limits.max_heap_bytes,
3973 });
3974 }
3975 Ok(())
3976 }
3977
3978 fn ensure_object_property_capacity(
3979 &self,
3980 property_bytes: usize,
3981 ) -> Result<(), RuntimeErrorKind> {
3982 let bytes =
3983 property_bytes
3984 .checked_add(1)
3985 .ok_or(RuntimeErrorKind::HeapByteLimitExceeded {
3986 limit: self.limits.max_heap_bytes,
3987 })?;
3988 self.ensure_allocation_capacity(1, bytes)
3989 }
3990 fn charge_heap(&mut self, bytes: usize) -> Result<(), RuntimeErrorKind> {
3991 self.ensure_allocation_capacity(0, bytes)?;
3992 self.heap_bytes += bytes;
3993 Ok(())
3994 }
3995
3996 fn runtime_slot(&self, value: Value) -> Result<Option<usize>, RuntimeErrorKind> {
3997 let Some(decoded) = value.decode() else {
3998 return Err(RuntimeErrorKind::InvalidValue { value });
3999 };
4000 let Decoded::HeapRef(id) = decoded else {
4001 return Ok(None);
4002 };
4003 if id.segment() != RUNTIME_HEAP_SEGMENT {
4004 return Err(RuntimeErrorKind::InvalidValue { value });
4005 }
4006 let index = id.slot() as usize - 1;
4007 if index >= self.heap.len() {
4008 return Err(RuntimeErrorKind::InvalidRuntimeHeapReference { slot: id.slot() });
4009 }
4010 Ok(Some(index))
4011 }
4012
4013 fn active_module_id(&self) -> ModuleId {
4014 self.frames
4015 .last()
4016 .map_or(ModuleId::new(0), |frame| frame.module)
4017 }
4018
4019 pub(crate) fn load_global(
4020 &self,
4021 module: ModuleId,
4022 name: ConstantId,
4023 ) -> Result<Option<Value>, RuntimeErrorKind> {
4024 if let Some(cell) = self
4025 .registry
4026 .modules
4027 .get(module.get() as usize)
4028 .and_then(|instance| instance.constant_cells.get(name.get() as usize))
4029 .copied()
4030 .flatten()
4031 {
4032 let value = self.registry.cells[cell.0].value;
4033 if value.is_uninitialized() {
4034 let binding = self.registry.modules[module.get() as usize]
4035 .binding_cells
4036 .iter()
4037 .position(|candidate| *candidate == Some(cell))
4038 .map(|index| BindingId::new(index as u32))
4039 .expect("linked cell belongs to a binding");
4040 return Err(RuntimeErrorKind::TemporalDeadZone { module, binding });
4041 }
4042 return Ok(Some(value));
4043 }
4044 Ok(self.resolve_global_binding(self.constant_text(module, name)))
4045 }
4046
4047 pub(crate) fn store_global(
4048 &mut self,
4049 module: ModuleId,
4050 name: ConstantId,
4051 value: Value,
4052 ) -> Result<(), EvalFailure> {
4053 let cell = self
4054 .registry
4055 .modules
4056 .get(module.get() as usize)
4057 .and_then(|instance| instance.constant_cells.get(name.get() as usize))
4058 .copied()
4059 .flatten();
4060 if let Some(cell) = cell {
4061 let binding = self.registry.modules[module.get() as usize]
4062 .binding_cells
4063 .iter()
4064 .position(|candidate| *candidate == Some(cell))
4065 .expect("mapped module cell belongs to a binding");
4066 if matches!(
4067 self.program_module(module).bindings[binding].kind,
4068 BindingKind::Imported { .. } | BindingKind::Namespace { .. }
4069 ) {
4070 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4071 operation: "assign to immutable module binding",
4072 }));
4073 }
4074 self.registry.cells[cell.0].value = value;
4075 } else {
4076 let name = self.constant_text(module, name).to_owned();
4077 if let Some(global_this) = self.intrinsics.global("globalThis") {
4078 let key = PropertyKey::Named(name.clone());
4079 if matches!(
4080 self.own_descriptor(global_this, &key)?,
4081 Some(
4082 Property::Data {
4083 writable: false,
4084 ..
4085 } | Property::Accessor { setter: None, .. }
4086 )
4087 ) {
4088 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4089 operation: "assign to non-writable global property",
4090 }));
4091 }
4092 }
4093 self.globals.insert(name, value);
4094 }
4095 Ok(())
4096 }
4097
4098 fn resolve_global_binding(&self, name: &EcmaString) -> Option<Value> {
4100 self.globals.get(name).copied().or_else(|| {
4101 self.intrinsics
4102 .globals
4103 .iter()
4104 .find_map(|(candidate, value)| (candidate == name).then_some(*value))
4105 })
4106 }
4107
4108 fn callee_kind(&self, callee: Value) -> Result<CalleeKind, RuntimeErrorKind> {
4110 match self.runtime_slot(callee)? {
4111 Some(index) => match &self.heap[index] {
4112 HeapEntry::Function {
4113 module,
4114 function,
4115 captures,
4116 ..
4117 } => Ok(CalleeKind::Runtime {
4118 target: RuntimeFunction {
4119 module: *module,
4120 function: *function,
4121 },
4122 captures: captures.clone(),
4123 }),
4124 HeapEntry::NativeFunction { callable, .. } => match callable {
4125 NativeCallable::Builtin(id) => Ok(CalleeKind::Builtin { id: *id }),
4126 NativeCallable::Bound(_) => Ok(CalleeKind::Bound),
4127 },
4128 _ => Ok(CalleeKind::NotCallable),
4129 },
4130 None => Ok(CalleeKind::NotCallable),
4131 }
4132 }
4133
4134 pub(crate) fn flatten_bound(
4135 &self,
4136 callee: Value,
4137 this_value: Value,
4138 arguments: &[Value],
4139 ) -> Result<BoundCall, RuntimeErrorKind> {
4140 let mut target = callee;
4141 let mut receiver = this_value;
4142 let mut segments = Vec::new();
4143 let mut total = arguments.len();
4144 while let Some(index) = self.runtime_slot(target)? {
4145 let HeapEntry::NativeFunction {
4146 callable: NativeCallable::Bound(bound),
4147 ..
4148 } = &self.heap[index]
4149 else {
4150 break;
4151 };
4152 total = total.checked_add(bound.arguments.len()).ok_or(
4153 RuntimeErrorKind::ArgumentLimitExceeded {
4154 limit: self.limits.max_argument_count,
4155 requested: u32::MAX,
4156 },
4157 )?;
4158 if total > self.limits.max_argument_count as usize {
4159 return Err(RuntimeErrorKind::ArgumentLimitExceeded {
4160 limit: self.limits.max_argument_count,
4161 requested: u32::try_from(total).unwrap_or(u32::MAX),
4162 });
4163 }
4164 segments.push(bound.arguments.as_slice());
4165 receiver = bound.this_value;
4166 target = bound.target;
4167 }
4168 let mut flattened = Vec::with_capacity(total);
4169 for segment in segments.iter().rev() {
4170 flattened.extend_from_slice(segment);
4171 }
4172 flattened.extend_from_slice(arguments);
4173 Ok(BoundCall {
4174 target,
4175 this_value: receiver,
4176 arguments: flattened,
4177 })
4178 }
4179
4180 fn bound_target(&self, mut value: Value) -> Result<Value, RuntimeErrorKind> {
4181 loop {
4182 let Some(index) = self.runtime_slot(value)? else {
4183 return Ok(value);
4184 };
4185 let HeapEntry::NativeFunction {
4186 callable: NativeCallable::Bound(bound),
4187 ..
4188 } = &self.heap[index]
4189 else {
4190 return Ok(value);
4191 };
4192 value = bound.target;
4193 }
4194 }
4195
4196 pub(crate) fn load_constant_value(
4199 &mut self,
4200 module: ModuleId,
4201 id: ConstantId,
4202 ) -> Result<Value, RuntimeErrorKind> {
4203 match &self.module_code(module).constants()[id.get() as usize] {
4204 Constant::String(text) => self.allocate(HeapEntry::String(text.clone())),
4205 Constant::BigInt(value) => self.allocate(HeapEntry::BigInt(value.as_str().to_owned())),
4206 constant => Ok(constant_value(constant).expect("non-heap constant")),
4207 }
4208 }
4209
4210 fn read_arguments(&self, frame: usize, register: u32) -> Result<Vec<Value>, EvalFailure> {
4213 let value = self.read_register(frame, register);
4214 self.arguments_from_array(value)
4215 }
4216
4217 fn arguments_from_array(&self, arguments: Value) -> Result<Vec<Value>, EvalFailure> {
4221 match self.runtime_slot(arguments).map_err(EvalFailure::Runtime)? {
4222 Some(index) => match &self.heap[index] {
4223 HeapEntry::Array { elements, .. } => {
4224 if elements.len() as u64 > u64::from(self.limits.max_argument_count) {
4225 return Err(EvalFailure::Runtime(
4226 RuntimeErrorKind::ArgumentLimitExceeded {
4227 limit: self.limits.max_argument_count,
4228 requested: u32::try_from(elements.len()).unwrap_or(u32::MAX),
4229 },
4230 ));
4231 }
4232 Ok(elements
4233 .iter()
4234 .map(|value| {
4235 if *value == Value::HOLE {
4236 Value::UNDEFINED
4237 } else {
4238 *value
4239 }
4240 })
4241 .collect())
4242 }
4243 _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4244 operation: "call arguments are not an array",
4245 })),
4246 },
4247 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4248 operation: "call arguments are not an array",
4249 })),
4250 }
4251 }
4252
4253 fn read_captures(
4256 &self,
4257 frame: usize,
4258 register: u32,
4259 function: FunctionId,
4260 ) -> Result<Vec<Value>, EvalFailure> {
4261 let value = self.read_register(frame, register);
4262 self.captures_from_array(self.active_module_id(), value, function)
4263 }
4264
4265 pub(crate) fn captures_from_array(
4269 &self,
4270 module: ModuleId,
4271 captures: Value,
4272 function: FunctionId,
4273 ) -> Result<Vec<Value>, EvalFailure> {
4274 let expected =
4275 self.module_code(module).functions()[function.get() as usize].capture_count() as usize;
4276 match self.runtime_slot(captures).map_err(EvalFailure::Runtime)? {
4277 Some(index) => match &self.heap[index] {
4278 HeapEntry::Array { elements, .. } => {
4279 if elements.len() != expected {
4280 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4281 operation: "closure capture array arity",
4282 }));
4283 }
4284 Ok(elements
4285 .iter()
4286 .map(|value| {
4287 if *value == Value::HOLE {
4288 Value::UNDEFINED
4289 } else {
4290 *value
4291 }
4292 })
4293 .collect())
4294 }
4295 _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4296 operation: "closure captures are not an array",
4297 })),
4298 },
4299 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4300 operation: "closure captures are not an array",
4301 })),
4302 }
4303 }
4304
4305 pub(crate) fn materialize_arguments(
4306 &mut self,
4307 frame: usize,
4308 function: usize,
4309 pc: usize,
4310 ) -> Result<Value, RuntimeError> {
4311 if let Some(existing) = self.frames[frame].arguments_object {
4312 return Ok(existing);
4313 }
4314 let args = self.frames[frame].args.clone();
4315 let value = self
4316 .allocate(HeapEntry::Array {
4317 elements: args,
4318 properties: PropertyMap::default(),
4319 prototype: Some(self.intrinsics.array_prototype),
4320 extensible: true,
4321 length_writable: true,
4322 })
4323 .map_err(|kind| self.error_at(kind, function, pc))?;
4324 self.frames[frame].arguments_object = Some(value);
4325 Ok(value)
4326 }
4327
4328 fn push_frame(
4329 &mut self,
4330 target: RuntimeFunction,
4331 captures: &[Value],
4332 this_value: Value,
4333 new_target: Value,
4334 arguments: &[Value],
4335 return_to: Option<ReturnTo>,
4336 ) -> Result<(), RuntimeError> {
4337 let function_index = target.function.get() as usize;
4338 let metadata = &self.module_code(target.module).functions()[function_index];
4339 let limit_error = |kind| match (self.frames.last(), return_to) {
4340 (Some(caller), Some(return_to)) => {
4341 self.error_at_in_module(kind, caller.module, caller.function, return_to.call_pc)
4342 }
4343 (_, None) => self.error_at_in_module(kind, target.module, function_index, 0),
4344 (None, Some(_)) => unreachable!("a returning frame has a caller"),
4345 };
4346 if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4347 return Err(limit_error(RuntimeErrorKind::CallDepthExceeded {
4348 limit: self.limits.max_call_depth,
4349 }));
4350 }
4351 let next_registers = metadata.register_count() as usize;
4352 if self.live_registers.saturating_add(next_registers) > self.limits.max_total_registers {
4353 return Err(limit_error(RuntimeErrorKind::RegisterLimitExceeded {
4354 limit: self.limits.max_total_registers,
4355 }));
4356 }
4357 let frame = Frame::new(
4358 target, metadata, captures, this_value, new_target, arguments, return_to,
4359 );
4360 self.live_registers += next_registers;
4361 self.frames.push(frame);
4362 Ok(())
4363 }
4364
4365 pub(crate) fn consume_fuel(&mut self, amount: u64) -> Result<(), RuntimeErrorKind> {
4366 if self.fuel < amount {
4367 self.fuel = 0;
4368 return Err(RuntimeErrorKind::FuelExhausted {
4369 limit: self.limits.fuel,
4370 });
4371 }
4372 self.fuel -= amount;
4373 Ok(())
4374 }
4375
4376 pub(crate) fn reserve_native_activation(
4377 &mut self,
4378 register_count: usize,
4379 ) -> Result<(), RuntimeErrorKind> {
4380 if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4381 return Err(RuntimeErrorKind::CallDepthExceeded {
4382 limit: self.limits.max_call_depth,
4383 });
4384 }
4385 if self.live_registers.saturating_add(register_count) > self.limits.max_total_registers {
4386 return Err(RuntimeErrorKind::RegisterLimitExceeded {
4387 limit: self.limits.max_total_registers,
4388 });
4389 }
4390 self.native_depth += 1;
4391 self.live_registers += register_count;
4392 Ok(())
4393 }
4394
4395 pub(crate) fn release_native_activation(&mut self, register_count: usize) {
4396 self.native_depth -= 1;
4397 self.live_registers -= register_count;
4398 }
4399
4400 pub(crate) fn reserve_suspended_activation_registers(
4401 &mut self,
4402 register_count: usize,
4403 ) -> Result<(), RuntimeErrorKind> {
4404 if self.live_registers.saturating_add(register_count) > self.limits.max_total_registers {
4405 return Err(RuntimeErrorKind::RegisterLimitExceeded {
4406 limit: self.limits.max_total_registers,
4407 });
4408 }
4409 self.live_registers += register_count;
4410 Ok(())
4411 }
4412
4413 pub(crate) fn release_suspended_activation_registers(&mut self, register_count: usize) {
4414 self.live_registers -= register_count;
4415 }
4416
4417 pub(crate) fn enter_native_generator(&mut self) -> Result<(), RuntimeErrorKind> {
4418 if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4419 return Err(RuntimeErrorKind::CallDepthExceeded {
4420 limit: self.limits.max_call_depth,
4421 });
4422 }
4423 self.native_depth += 1;
4424 Ok(())
4425 }
4426
4427 pub(crate) fn leave_native_generator(&mut self) {
4428 self.native_depth -= 1;
4429 }
4430
4431 fn execute_call(&mut self, request: CallRequest<'_>) -> Result<(), RuntimeError> {
4432 let CallRequest {
4433 callee,
4434 this_value,
4435 arguments,
4436 destination,
4437 call_pc,
4438 constructed,
4439 new_target,
4440 } = request;
4441 let mut callee = callee;
4442 let mut this_value = this_value;
4443 let mut arguments = Cow::Borrowed(arguments);
4444 loop {
4445 match self.callee_kind(callee) {
4446 Ok(CalleeKind::Runtime { target, captures }) => {
4447 let flags = self.module_code(target.module).functions()
4448 [target.function.get() as usize]
4449 .flags();
4450 if flags.is_generator && !flags.is_async {
4451 let generator = self
4452 .create_generator(GeneratorStart {
4453 target,
4454 captures,
4455 this_value,
4456 new_target,
4457 args: arguments.as_ref().to_vec(),
4458 })
4459 .map_err(|kind| self.error_here_at(kind, call_pc))?;
4460 if let Some(register) = destination {
4461 self.write_register(self.frames.len() - 1, register, generator);
4462 }
4463 return Ok(());
4464 }
4465 if flags.is_async && !flags.is_generator {
4466 return match self.start_async_call(
4467 target,
4468 &captures,
4469 this_value,
4470 new_target,
4471 arguments.as_ref(),
4472 ) {
4473 Ok(promise) => {
4474 if let Some(register) = destination {
4475 self.write_register(self.frames.len() - 1, register, promise);
4476 }
4477 Ok(())
4478 }
4479 Err(failure) => self.resolve_failure(failure, call_pc),
4480 };
4481 }
4482 return self.push_frame(
4483 target,
4484 &captures,
4485 this_value,
4486 new_target,
4487 arguments.as_ref(),
4488 Some(ReturnTo {
4489 destination: destination.map(|register| register as usize),
4490 call_pc,
4491 constructed,
4492 }),
4493 );
4494 }
4495 Ok(CalleeKind::Builtin { id }) => {
4496 match self.call_builtin(id, this_value, arguments.as_ref(), false) {
4497 Ok(intrinsics::BuiltinOutcome::Value(value)) => {
4498 if let Some(register) = destination {
4499 self.write_register(self.frames.len() - 1, register, value);
4500 }
4501 return Ok(());
4502 }
4503 Ok(intrinsics::BuiltinOutcome::Call {
4504 callee: next,
4505 this_value: next_this,
4506 arguments: next_arguments,
4507 }) => {
4508 callee = next;
4509 this_value = next_this;
4510 arguments = Cow::Owned(next_arguments);
4511 }
4512 Ok(intrinsics::BuiltinOutcome::GeneratorNext {
4513 generator,
4514 resume_value,
4515 }) => match self.resume_generator(generator, resume_value) {
4516 Ok(value) => {
4517 if let Some(register) = destination {
4518 self.write_register(self.frames.len() - 1, register, value);
4519 }
4520 return Ok(());
4521 }
4522 Err(failure) => return self.resolve_failure(failure, call_pc),
4523 },
4524 Ok(intrinsics::BuiltinOutcome::ConstructCall { .. }) => {
4525 return self.throw_type("call", call_pc);
4526 }
4527 Err(failure) => return self.resolve_failure(failure, call_pc),
4528 }
4529 }
4530 Ok(CalleeKind::Bound) => {
4531 let bound = self
4532 .flatten_bound(callee, this_value, arguments.as_ref())
4533 .map_err(|kind| self.error_here_at(kind, call_pc))?;
4534 callee = bound.target;
4535 if constructed.is_none() {
4536 this_value = bound.this_value;
4537 }
4538 arguments = Cow::Owned(bound.arguments);
4539 }
4540 Ok(CalleeKind::NotCallable) => return self.throw_type("call", call_pc),
4541 Err(kind) => return Err(self.error_here_at(kind, call_pc)),
4542 }
4543 }
4544 }
4545
4546 fn execute_construct(
4547 &mut self,
4548 callee: Value,
4549 arguments: &[Value],
4550 destination: u32,
4551 call_pc: usize,
4552 ) -> Result<(), RuntimeError> {
4553 let mut callee = callee;
4554 let mut arguments = Cow::Borrowed(arguments);
4555 if matches!(self.callee_kind(callee), Ok(CalleeKind::Bound)) {
4556 let bound = self
4557 .flatten_bound(callee, Value::UNDEFINED, arguments.as_ref())
4558 .map_err(|kind| self.error_here_at(kind, call_pc))?;
4559 callee = bound.target;
4560 arguments = Cow::Owned(bound.arguments);
4561 }
4562 let index = match self.runtime_slot(callee) {
4563 Ok(Some(index)) => index,
4564 Ok(None) => return self.throw_type("construct", call_pc),
4565 Err(kind) => return Err(self.error_here_at(kind, call_pc)),
4566 };
4567 let builtin = match &self.heap[index] {
4568 HeapEntry::NativeFunction {
4569 callable: NativeCallable::Builtin(id),
4570 ..
4571 } => Some(*id),
4572 _ => None,
4573 };
4574 if let Some(id) = builtin {
4575 return match self.call_builtin(id, Value::UNDEFINED, arguments.as_ref(), true) {
4576 Ok(intrinsics::BuiltinOutcome::Value(value)) => {
4577 self.write_register(self.frames.len() - 1, destination, value);
4578 Ok(())
4579 }
4580 Ok(
4581 intrinsics::BuiltinOutcome::Call { .. }
4582 | intrinsics::BuiltinOutcome::GeneratorNext { .. },
4583 ) => self.throw_type("construct", call_pc),
4584 Ok(intrinsics::BuiltinOutcome::ConstructCall {
4585 callee: continuation,
4586 this_value,
4587 arguments: continuation_arguments,
4588 prototype,
4589 }) => {
4590 let object = self
4591 .allocate_constructed_receiver_with(prototype)
4592 .map_err(|kind| self.error_here_at(kind, call_pc))?;
4593 self.execute_call(CallRequest {
4594 callee: continuation,
4595 this_value,
4596 arguments: &continuation_arguments,
4597 destination: Some(destination),
4598 call_pc,
4599 constructed: Some(object),
4600 new_target: callee,
4601 })
4602 }
4603 Err(failure) => self.resolve_failure(failure, call_pc),
4604 };
4605 }
4606 if !matches!(
4607 self.heap[index],
4608 HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. }
4609 ) {
4610 return self.throw_type("construct", call_pc);
4611 }
4612 if let HeapEntry::Function {
4613 module, function, ..
4614 } = self.heap[index]
4615 {
4616 if self.module_code(module).functions()[function.get() as usize]
4617 .flags()
4618 .is_async
4619 {
4620 return self.throw_type("construct", call_pc);
4621 }
4622 }
4623 let object = self
4624 .allocate_constructed_receiver(callee)
4625 .map_err(|kind| self.error_here_at(kind, call_pc))?;
4626 self.execute_call(CallRequest {
4627 callee,
4628 this_value: object,
4629 arguments: arguments.as_ref(),
4630 destination: Some(destination),
4631 call_pc,
4632 constructed: Some(object),
4633 new_target: callee,
4634 })
4635 }
4636
4637 fn constructed_prototype(&self, callee: Value) -> Result<Value, RuntimeErrorKind> {
4638 let index = self
4639 .runtime_slot(callee)?
4640 .ok_or(RuntimeErrorKind::InvalidValue { value: callee })?;
4641 Ok(match self.own_data_property(index, "prototype") {
4642 Some(value) if self.is_object(value) => value,
4643 _ => self.intrinsics.object_prototype,
4644 })
4645 }
4646
4647 fn allocate_constructed_receiver(&mut self, callee: Value) -> Result<Value, RuntimeErrorKind> {
4648 let prototype = self.constructed_prototype(callee)?;
4649 self.allocate_constructed_receiver_with(prototype)
4650 }
4651
4652 fn allocate_constructed_receiver_with(
4653 &mut self,
4654 prototype: Value,
4655 ) -> Result<Value, RuntimeErrorKind> {
4656 self.allocate(HeapEntry::Object {
4657 properties: PropertyMap::default(),
4658 prototype: Some(prototype),
4659 boxed_primitive: None,
4660 extensible: true,
4661 })
4662 }
4663
4664 pub(crate) fn array_elements(&self, value: Value) -> Result<Option<Vec<Value>>, EvalFailure> {
4665 let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4666 return Ok(None);
4667 };
4668 match &self.heap[index] {
4669 HeapEntry::Array { elements, .. } => Ok(Some(elements.clone())),
4670 _ => Ok(None),
4671 }
4672 }
4673
4674 pub(crate) fn array_length(&self, value: Value) -> Result<usize, EvalFailure> {
4675 self.array_elements(value)?
4676 .map(|elements| elements.len())
4677 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
4678 operation: "array method called on incompatible receiver",
4679 }))
4680 }
4681
4682 pub(crate) fn replace_array_elements(
4683 &mut self,
4684 value: Value,
4685 elements: Vec<Value>,
4686 ) -> Result<(), EvalFailure> {
4687 let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4688 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4689 operation: "array method called on incompatible receiver",
4690 }));
4691 };
4692 let HeapEntry::Array {
4693 elements: current, ..
4694 } = &mut self.heap[index]
4695 else {
4696 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4697 operation: "array method called on incompatible receiver",
4698 }));
4699 };
4700 *current = elements;
4701 Ok(())
4702 }
4703
4704 pub(crate) fn string_value(&self, value: Value) -> Option<EcmaString> {
4705 let index = self.runtime_slot(value).ok().flatten()?;
4706 match &self.heap[index] {
4707 HeapEntry::String(text) => Some(text.clone()),
4708 _ => None,
4709 }
4710 }
4711
4712 pub(crate) fn get_named_property(
4713 &mut self,
4714 object: Value,
4715 name: &str,
4716 ) -> Result<Value, EvalFailure> {
4717 self.get_property_ascii(object, name)
4718 }
4719
4720 fn get_property_ascii(&mut self, object: Value, name: &str) -> Result<Value, EvalFailure> {
4721 debug_assert!(name.is_ascii());
4722 match self.resolve_get_ascii(object, name)? {
4723 GetOutcome::Value(value) => Ok(value),
4724 GetOutcome::Text(text) => self
4725 .allocate(HeapEntry::String(text))
4726 .map_err(EvalFailure::Runtime),
4727 GetOutcome::Getter(getter) => self.call_value(getter, object, &[]),
4728 }
4729 }
4730
4731 pub(crate) fn get_property_key(
4732 &mut self,
4733 object: Value,
4734 key: &PropertyKey,
4735 ) -> Result<Value, EvalFailure> {
4736 match self.resolve_get(object, key)? {
4737 GetOutcome::Value(value) => Ok(value),
4738 GetOutcome::Text(text) => self
4739 .allocate(HeapEntry::String(text))
4740 .map_err(EvalFailure::Runtime),
4741 GetOutcome::Getter(getter) => self.call_value(getter, object, &[]),
4742 }
4743 }
4744
4745 pub(crate) fn set_data_property(
4746 &mut self,
4747 object: Value,
4748 name: &str,
4749 value: Value,
4750 ) -> Result<(), EvalFailure> {
4751 self.set_data_property_key(
4752 object,
4753 PropertyKey::Named(EcmaString::from_utf8(name)),
4754 value,
4755 )
4756 }
4757
4758 pub(crate) fn set_data_property_key(
4759 &mut self,
4760 object: Value,
4761 key: PropertyKey,
4762 value: Value,
4763 ) -> Result<(), EvalFailure> {
4764 match self.resolve_set(object, key, value)? {
4765 SetOutcome::Done => Ok(()),
4766 SetOutcome::Setter(setter) => {
4767 self.call_value(setter, object, &[value])?;
4768 Ok(())
4769 }
4770 }
4771 }
4772
4773 pub(crate) fn is_callable(&self, value: Value) -> Result<bool, EvalFailure> {
4774 Ok(!matches!(
4775 self.callee_kind(value).map_err(EvalFailure::Runtime)?,
4776 CalleeKind::NotCallable
4777 ))
4778 }
4779
4780 pub(crate) fn box_primitive(&mut self, value: Value) -> Result<Value, EvalFailure> {
4781 let prototype = match value.decode() {
4782 Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
4783 Some(Decoded::Number(_) | Decoded::Int32(_)) => self.intrinsics.number_prototype,
4784 Some(Decoded::HeapRef(_)) if self.string_value(value).is_some() => {
4785 self.intrinsics.string_prototype
4786 }
4787 _ => self.intrinsics.object_prototype,
4788 };
4789 self.allocate(HeapEntry::Object {
4790 properties: PropertyMap::default(),
4791 prototype: Some(prototype),
4792 boxed_primitive: Some(value),
4793 extensible: true,
4794 })
4795 .map_err(EvalFailure::Runtime)
4796 }
4797
4798 pub(crate) fn unbox_primitive_or_self(&self, value: Value) -> Result<Value, EvalFailure> {
4799 let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4800 return Ok(value);
4801 };
4802 match self.heap[index] {
4803 HeapEntry::Object {
4804 boxed_primitive: Some(primitive),
4805 ..
4806 } => Ok(primitive),
4807 _ => Ok(value),
4808 }
4809 }
4810
4811 pub(crate) fn unbox_primitive(
4812 &self,
4813 value: Value,
4814 operation: &'static str,
4815 ) -> Result<Value, EvalFailure> {
4816 let unboxed = self.unbox_primitive_or_self(value)?;
4817 if unboxed == value && self.is_object(value) {
4818 Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }))
4819 } else {
4820 Ok(unboxed)
4821 }
4822 }
4823
4824 pub(crate) fn current_builtin_id(&self) -> Option<intrinsics::BuiltinId> {
4825 self.current_builtin_id
4826 }
4827
4828 pub(crate) fn throw_error(
4829 &mut self,
4830 id: intrinsics::BuiltinId,
4831 message: String,
4832 ) -> EvalFailure {
4833 let message = match self.allocate(HeapEntry::String(EcmaString::from_utf8(&message))) {
4834 Ok(value) => value,
4835 Err(kind) => return EvalFailure::Runtime(kind),
4836 };
4837 let mut properties = PropertyMap::default();
4838 properties.insert(
4839 PropertyKey::Named(EcmaString::from_utf8("message")),
4840 Property::Data {
4841 value: message,
4842 writable: true,
4843 enumerable: true,
4844 configurable: true,
4845 },
4846 );
4847 match self.allocate(HeapEntry::Object {
4848 properties,
4849 prototype: Some(self.intrinsics.error_prototype(id)),
4850 boxed_primitive: None,
4851 extensible: true,
4852 }) {
4853 Ok(value) => EvalFailure::ThrowValue(value),
4854 Err(kind) => EvalFailure::Runtime(kind),
4855 }
4856 }
4857
4858 pub(crate) fn has_own_property_key(
4859 &self,
4860 object: Value,
4861 key: &PropertyKey,
4862 ) -> Result<bool, EvalFailure> {
4863 let Some(index) = self.runtime_slot(object).map_err(EvalFailure::Runtime)? else {
4864 return Ok(false);
4865 };
4866 Ok(self.own_get(index, key).is_some())
4867 }
4868
4869 pub(crate) fn call_value(
4870 &mut self,
4871 callee: Value,
4872 this_value: Value,
4873 arguments: &[Value],
4874 ) -> Result<Value, EvalFailure> {
4875 let mut callee = callee;
4876 let mut this_value = this_value;
4877 let mut arguments = Cow::Borrowed(arguments);
4878 loop {
4879 match self.callee_kind(callee).map_err(EvalFailure::Runtime)? {
4880 CalleeKind::Builtin { id } => {
4881 match self.call_builtin(id, this_value, arguments.as_ref(), false)? {
4882 intrinsics::BuiltinOutcome::Value(value) => return Ok(value),
4883 intrinsics::BuiltinOutcome::Call {
4884 callee: next,
4885 this_value: next_this,
4886 arguments: next_arguments,
4887 } => {
4888 callee = next;
4889 this_value = next_this;
4890 arguments = Cow::Owned(next_arguments);
4891 }
4892 intrinsics::BuiltinOutcome::GeneratorNext {
4893 generator,
4894 resume_value,
4895 } => return self.resume_generator(generator, resume_value),
4896 intrinsics::BuiltinOutcome::ConstructCall { .. } => {
4897 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4898 operation: "call",
4899 }));
4900 }
4901 }
4902 }
4903 CalleeKind::Runtime { target, captures } => {
4904 let flags = self.module_code(target.module).functions()
4905 [target.function.get() as usize]
4906 .flags();
4907 if flags.is_generator && !flags.is_async {
4908 return self
4909 .create_generator(GeneratorStart {
4910 target,
4911 captures,
4912 this_value,
4913 new_target: Value::UNDEFINED,
4914 args: arguments.as_ref().to_vec(),
4915 })
4916 .map_err(EvalFailure::Runtime);
4917 }
4918 if flags.is_async && !flags.is_generator {
4919 return self.start_async_call(
4920 target,
4921 &captures,
4922 this_value,
4923 Value::UNDEFINED,
4924 arguments.as_ref(),
4925 );
4926 }
4927 let stop_depth = self.frames.len();
4928 let return_to = self.frames.last().map(|frame| ReturnTo {
4929 destination: None,
4930 call_pc: frame.pc,
4931 constructed: None,
4932 });
4933 self.push_frame(
4934 target,
4935 &captures,
4936 this_value,
4937 Value::UNDEFINED,
4938 arguments.as_ref(),
4939 return_to,
4940 )
4941 .map_err(|error| EvalFailure::Runtime(error.kind))?;
4942 self.callback_boundaries.push(stop_depth);
4943 let result = self.run_loop(stop_depth);
4944 self.callback_boundaries
4945 .pop()
4946 .expect("nested runtime callback owns its unwind boundary");
4947 return match result {
4948 Ok(None) => self.last_completion.take().ok_or(EvalFailure::Runtime(
4949 RuntimeErrorKind::InvalidValue {
4950 value: Value::UNDEFINED,
4951 },
4952 )),
4953 Ok(Some(execution)) => Ok(execution.value),
4954 Err(error) => {
4955 self.unwind_frames_to(stop_depth);
4956 match error.kind {
4957 RuntimeErrorKind::UncaughtThrow { value, .. } => {
4958 Err(EvalFailure::ThrowValue(value))
4959 }
4960 kind => Err(EvalFailure::Runtime(kind)),
4961 }
4962 }
4963 };
4964 }
4965 CalleeKind::Bound => {
4966 let bound = self
4967 .flatten_bound(callee, this_value, arguments.as_ref())
4968 .map_err(EvalFailure::Runtime)?;
4969 callee = bound.target;
4970 this_value = bound.this_value;
4971 arguments = Cow::Owned(bound.arguments);
4972 }
4973 CalleeKind::NotCallable => {
4974 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4975 operation: "call",
4976 }));
4977 }
4978 }
4979 }
4980 }
4981
4982 fn unwind_frames_to(&mut self, depth: usize) {
4983 while self.frames.len() > depth {
4984 let frame = self.frames.pop().expect("frame depth was checked");
4985 self.live_registers -= frame.registers.len();
4986 }
4987 }
4988
4989 fn complete_frame(&mut self, returned: Value) -> Option<Execution> {
4990 let frame = self.frames.pop().expect("an activation is executing");
4991 self.live_registers -= frame.registers.len();
4992 match frame.return_to {
4993 None => {
4994 let outcome = ExecutionOutcome {
4995 stdout: Vec::new(),
4996 exit_code: 0,
4997 };
4998 Some(Execution {
4999 outcome,
5000 value: returned,
5001 link: returned,
5002 entry_registers: frame.registers,
5003 })
5004 }
5005 Some(return_to) => {
5006 let value = match return_to.constructed {
5007 Some(object) if !self.is_object(returned) => object,
5008 _ => returned,
5009 };
5010 if let Some(destination) = return_to.destination {
5011 self.frames.last_mut().expect("callee has caller").registers[destination] =
5012 value;
5013 } else {
5014 self.last_completion = Some(value);
5015 }
5016 None
5017 }
5018 }
5019 }
5020
5021 fn resolve_failure(&mut self, failure: EvalFailure, pc: usize) -> Result<(), RuntimeError> {
5022 match failure {
5023 EvalFailure::Throw(origin) => self.throw(Value::UNDEFINED, origin, pc),
5024 EvalFailure::ThrowValue(value) => self.throw(value, ThrowOrigin::Bytecode, pc),
5025 EvalFailure::ThrowValueOrigin { value, origin } => self.throw(value, origin, pc),
5026 EvalFailure::Runtime(kind) => Err(self.error_here_at(kind, pc)),
5027 }
5028 }
5029
5030 fn throw_type(&mut self, operation: &'static str, pc: usize) -> Result<(), RuntimeError> {
5031 self.throw(Value::UNDEFINED, ThrowOrigin::TypeError { operation }, pc)
5032 }
5033
5034 fn throw(
5035 &mut self,
5036 value: Value,
5037 origin: ThrowOrigin,
5038 faulting_pc: usize,
5039 ) -> Result<(), RuntimeError> {
5040 let site_module = self
5041 .frames
5042 .last()
5043 .expect("an activation is executing")
5044 .module;
5045 let site_function = self
5046 .frames
5047 .last()
5048 .expect("an activation is executing")
5049 .function;
5050 let mut search_pc = faulting_pc;
5051 loop {
5052 if self
5053 .callback_boundaries
5054 .last()
5055 .is_some_and(|boundary| self.frames.len() == *boundary)
5056 {
5057 return Err(self.error_at_in_module(
5058 RuntimeErrorKind::UncaughtThrow { value, origin },
5059 site_module,
5060 site_function,
5061 faulting_pc,
5062 ));
5063 }
5064 let frame_index = self.frames.len() - 1;
5065 let function_index = self.frames[frame_index].function;
5066 let module = self.frames[frame_index].module;
5067 let function = &self.module_code(module).functions()[function_index];
5068 if let Some(handler) = innermost_handler(function, search_pc) {
5069 let frame = &mut self.frames[frame_index];
5070 frame.registers[handler.catch_register.get() as usize] = value;
5071 frame.pc = handler.handler.get() as usize;
5072 return Ok(());
5073 }
5074 let frame = self.frames.pop().expect("throw walks live frames");
5075 self.live_registers -= frame.registers.len();
5076 match frame.return_to {
5077 Some(return_to) => search_pc = return_to.call_pc,
5078 None => {
5079 return Err(self.error_at_in_module(
5080 RuntimeErrorKind::UncaughtThrow { value, origin },
5081 site_module,
5082 site_function,
5083 faulting_pc,
5084 ));
5085 }
5086 }
5087 }
5088 }
5089
5090 fn error_here(&self, kind: RuntimeErrorKind) -> RuntimeError {
5091 let frame = self.frames.last().expect("an activation is executing");
5092 self.error_at(kind, frame.function, frame.pc)
5093 }
5094
5095 fn error_here_at(&self, kind: RuntimeErrorKind, pc: usize) -> RuntimeError {
5096 let function = self
5097 .frames
5098 .last()
5099 .expect("an activation is executing")
5100 .function;
5101 self.error_at(kind, function, pc)
5102 }
5103
5104 fn error_at(&self, kind: RuntimeErrorKind, function: usize, pc: usize) -> RuntimeError {
5105 self.error_at_in_module(kind, self.active_module_id(), function, pc)
5106 }
5107
5108 pub(crate) fn error_at_in_module(
5109 &self,
5110 kind: RuntimeErrorKind,
5111 module: ModuleId,
5112 function: usize,
5113 pc: usize,
5114 ) -> RuntimeError {
5115 let code = self.module_code(module);
5116 let metadata = &code.functions()[function];
5117 let function_name =
5118 metadata
5119 .name()
5120 .and_then(|id| match &code.constants()[id.get() as usize] {
5121 Constant::String(name) => Some(name.clone()),
5122 _ => None,
5123 });
5124 RuntimeError {
5125 kind,
5126 function: FunctionId::new(function as u32),
5127 pc: Pc::new(pc as u32),
5128 source: RuntimeSource {
5129 function_name,
5130 instruction: metadata.code()[pc],
5131 },
5132 }
5133 }
5134
5135 fn to_property_key(&self, value: Value) -> Result<PropertyKey, EvalFailure> {
5141 match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
5142 Some(index) => match &self.heap[index] {
5143 HeapEntry::String(text) => Ok(PropertyKey::Named(text.clone())),
5144 HeapEntry::Symbol { .. } => Ok(PropertyKey::Symbol(index as u32)),
5145 HeapEntry::PrivateName { .. } => Ok(PropertyKey::Private(index as u32)),
5146 _ => Ok(PropertyKey::Named(self.value_to_string(value, 0)?)),
5147 },
5148 None => Ok(PropertyKey::Named(self.value_to_string(value, 0)?)),
5149 }
5150 }
5151
5152 fn resolve_get(&mut self, object: Value, key: &PropertyKey) -> Result<GetOutcome, EvalFailure> {
5155 let slot = self.runtime_slot(object).map_err(EvalFailure::Runtime)?;
5156 let start = match slot {
5157 Some(index) => {
5158 if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5159 let PropertyKey::Named(name) = key else {
5160 return Ok(GetOutcome::Value(Value::UNDEFINED));
5161 };
5162 let text = name
5163 .to_utf8_strict()
5164 .ok()
5165 .and_then(|name| self.host.env(&name))
5166 .map(EcmaString::from_utf8);
5167 return match text {
5168 Some(text) => self
5169 .allocate(HeapEntry::String(text))
5170 .map(GetOutcome::Value)
5171 .map_err(EvalFailure::Runtime),
5172 None => Ok(GetOutcome::Value(Value::UNDEFINED)),
5173 };
5174 }
5175 if let Some(found) = self.primitive_get(index, key) {
5176 return self.found_outcome(found);
5177 }
5178 match self.heap[index] {
5179 HeapEntry::String(_) => self
5180 .runtime_slot(self.intrinsics.string_prototype)
5181 .map_err(EvalFailure::Runtime)?,
5182 HeapEntry::BigInt(_) | HeapEntry::PrivateName { .. } => self
5183 .runtime_slot(self.intrinsics.object_prototype)
5184 .map_err(EvalFailure::Runtime)?,
5185 HeapEntry::Symbol { .. } => self
5186 .runtime_slot(self.intrinsics.builtins.symbol_prototype())
5187 .map_err(EvalFailure::Runtime)?,
5188 _ => Some(index),
5189 }
5190 }
5191 None => {
5192 let prototype = match object.decode() {
5193 Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
5194 Some(Decoded::Number(_) | Decoded::Int32(_)) => {
5195 self.intrinsics.number_prototype
5196 }
5197 _ => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5198 };
5199 self.runtime_slot(prototype).map_err(EvalFailure::Runtime)?
5200 }
5201 };
5202 let Some(mut node) = start else {
5203 return Ok(GetOutcome::Value(Value::UNDEFINED));
5204 };
5205 for _ in 0..=self.heap.len() {
5206 if let Some(found) = self.own_get(node, key) {
5207 return self.found_outcome(found);
5208 }
5209 match self.prototype_index(node)? {
5210 Some(next) => node = next,
5211 None => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5212 }
5213 }
5214 Ok(GetOutcome::Value(Value::UNDEFINED))
5215 }
5216
5217 fn resolve_get_ascii(&mut self, object: Value, name: &str) -> Result<GetOutcome, EvalFailure> {
5218 debug_assert!(name.is_ascii());
5219 let slot = self.runtime_slot(object).map_err(EvalFailure::Runtime)?;
5220 let start = match slot {
5221 Some(index) => {
5222 if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5223 return match self.host.env(name).map(EcmaString::from_utf8) {
5224 Some(text) => self
5225 .allocate(HeapEntry::String(text))
5226 .map(GetOutcome::Value)
5227 .map_err(EvalFailure::Runtime),
5228 None => Ok(GetOutcome::Value(Value::UNDEFINED)),
5229 };
5230 }
5231 if let HeapEntry::String(text) = &self.heap[index] {
5232 if name == "length" {
5233 return Ok(GetOutcome::Value(number_value(text.len_units() as f64)));
5234 }
5235 if let Some(offset) = array_index_ascii(name)
5236 && let Some(unit) = text.unit_at(offset as usize)
5237 {
5238 return Ok(GetOutcome::Text(EcmaString::from_units(&[unit])));
5239 }
5240 }
5241 match self.heap[index] {
5242 HeapEntry::String(_) => self
5243 .runtime_slot(self.intrinsics.string_prototype)
5244 .map_err(EvalFailure::Runtime)?,
5245 HeapEntry::BigInt(_) | HeapEntry::PrivateName { .. } => self
5246 .runtime_slot(self.intrinsics.object_prototype)
5247 .map_err(EvalFailure::Runtime)?,
5248 HeapEntry::Symbol { .. } => self
5249 .runtime_slot(self.intrinsics.builtins.symbol_prototype())
5250 .map_err(EvalFailure::Runtime)?,
5251 _ => Some(index),
5252 }
5253 }
5254 None => {
5255 let prototype = match object.decode() {
5256 Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
5257 Some(Decoded::Number(_) | Decoded::Int32(_)) => {
5258 self.intrinsics.number_prototype
5259 }
5260 _ => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5261 };
5262 self.runtime_slot(prototype).map_err(EvalFailure::Runtime)?
5263 }
5264 };
5265 let Some(mut node) = start else {
5266 return Ok(GetOutcome::Value(Value::UNDEFINED));
5267 };
5268 for _ in 0..=self.heap.len() {
5269 if let Some(found) = self.own_get_ascii(node, name) {
5270 return self.found_outcome(found);
5271 }
5272 match self.prototype_index(node)? {
5273 Some(next) => node = next,
5274 None => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5275 }
5276 }
5277 Ok(GetOutcome::Value(Value::UNDEFINED))
5278 }
5279
5280 fn found_outcome(&mut self, found: Found) -> Result<GetOutcome, EvalFailure> {
5281 match found {
5282 Found::Value(Value::UNINITIALIZED) => {
5283 let id = self
5284 .intrinsics
5285 .builtins
5286 .id_named("ReferenceError")
5287 .expect("ReferenceError intrinsic is installed");
5288 match self.throw_error(
5289 id,
5290 "Cannot access lexical binding before initialization".into(),
5291 ) {
5292 EvalFailure::ThrowValue(value) => Err(EvalFailure::ThrowValueOrigin {
5293 value,
5294 origin: ThrowOrigin::ReferenceError {
5295 operation: "lexical binding is uninitialized",
5296 },
5297 }),
5298 failure => Err(failure),
5299 }
5300 }
5301 Found::Value(value) => Ok(GetOutcome::Value(value)),
5302 Found::Text(text) => Ok(GetOutcome::Text(text)),
5303 Found::Getter(getter) => Ok(GetOutcome::Getter(getter)),
5304 Found::Failure(kind) => Err(EvalFailure::Runtime(kind)),
5305 Found::NoGetter => Ok(GetOutcome::Value(Value::UNDEFINED)),
5306 }
5307 }
5308
5309 fn primitive_get(&self, index: usize, key: &PropertyKey) -> Option<Found> {
5310 if let HeapEntry::String(text) = &self.heap[index]
5311 && let PropertyKey::Named(name) = key
5312 {
5313 if name.eq_ascii("length") {
5314 return Some(Found::Value(number_value(text.len_units() as f64)));
5315 }
5316 if let Some(offset) = array_index(name)
5317 && let Some(unit) = text.unit_at(offset as usize)
5318 {
5319 return Some(Found::Text(EcmaString::from_units(&[unit])));
5320 }
5321 }
5322 None
5323 }
5324 fn own_get_ascii(&self, index: usize, name: &str) -> Option<Found> {
5325 debug_assert!(name.is_ascii());
5326 let slot = |value| self.runtime_slot(value).ok().flatten();
5327 if slot(self.intrinsics.object_prototype) == Some(index) && name == "toString" {
5328 return Some(Found::Value(self.intrinsics.object_to_string()));
5329 }
5330 match &self.heap[index] {
5331 HeapEntry::Object { properties, .. }
5332 | HeapEntry::Generator { properties, .. }
5333 | HeapEntry::Script { properties, .. }
5334 | HeapEntry::NativeFunction { properties, .. }
5335 | HeapEntry::Date { properties, .. }
5336 | HeapEntry::BuiltinIterator { properties, .. }
5337 | HeapEntry::Collection { properties, .. }
5338 | HeapEntry::Promise { properties, .. }
5339 | HeapEntry::Timeout { properties, .. } => property_lookup_ascii(properties, name),
5340 HeapEntry::Array {
5341 elements,
5342 properties,
5343 ..
5344 } => {
5345 if name == "length" {
5346 return Some(Found::Value(number_value(elements.len() as f64)));
5347 }
5348 if let Some(offset) = array_index_ascii(name)
5349 && let Some(element) = elements.get(offset as usize)
5350 && *element != Value::HOLE
5351 {
5352 return Some(Found::Value(*element));
5353 }
5354 property_lookup_ascii(properties, name)
5355 }
5356 HeapEntry::Function {
5357 module,
5358 function,
5359 properties,
5360 ..
5361 } => {
5362 if let Some(found) = property_lookup_ascii(properties, name) {
5363 return Some(found);
5364 }
5365 let metadata = &self.module_code(*module).functions()[function.get() as usize];
5366 if name == "length" {
5367 return Some(Found::Value(
5368 number_value(metadata.parameter_count() as f64),
5369 ));
5370 }
5371 if name == "name" {
5372 return Some(Found::Text(
5373 metadata
5374 .name()
5375 .map(|id| self.constant_text(*module, id).clone())
5376 .unwrap_or_default(),
5377 ));
5378 }
5379 None
5380 }
5381 HeapEntry::ModuleNamespace { module } => {
5382 let key = self
5383 .program_module(*module)
5384 .exports
5385 .iter()
5386 .map(|export| self.constant_text(*module, export.name))
5387 .find(|candidate| candidate.eq_ascii(name))?
5388 .clone();
5389 match self.namespace_export(*module, &key) {
5390 Ok(Some(value)) => Some(Found::Value(value)),
5391 Ok(None) => None,
5392 Err(kind) => Some(Found::Failure(kind)),
5393 }
5394 }
5395 HeapEntry::ExternalModuleNamespace { specifier } => {
5396 let export = self.registry.external[specifier]
5397 .exports
5398 .iter()
5399 .find_map(|(candidate, export)| candidate.eq_ascii(name).then_some(export))?;
5400 let cell = export
5401 .cell
5402 .expect("external namespace exports link before evaluation");
5403 Some(Found::Value(self.registry.cells[cell.0].value))
5404 }
5405 HeapEntry::RegExp {
5406 pattern,
5407 flags,
5408 properties,
5409 ..
5410 } => {
5411 if let Some(found) = property_lookup_ascii(properties, name) {
5412 return Some(found);
5413 }
5414 let flag = |unit| {
5415 Found::Value(Value::boolean(flags.as_units().contains(&u16::from(unit))))
5416 };
5417 match name {
5418 "source" => Some(Found::Text(crate::intrinsics::builtins::canonical_source(
5419 pattern,
5420 ))),
5421 "flags" => Some(Found::Text(flags.clone())),
5422 "global" => Some(flag(b'g')),
5423 "ignoreCase" => Some(flag(b'i')),
5424 "multiline" => Some(flag(b'm')),
5425 "sticky" => Some(flag(b'y')),
5426 "unicode" => Some(flag(b'u')),
5427 "dotAll" => Some(flag(b's')),
5428 "lastIndex" => Some(Found::Value(Value::int32(0))),
5429 _ => None,
5430 }
5431 }
5432 HeapEntry::HashState { update, digest, .. } => match name {
5433 "update" => Some(Found::Value(*update)),
5434 "digest" => Some(Found::Value(*digest)),
5435 _ => None,
5436 },
5437 HeapEntry::ProcessEnv { .. }
5438 | HeapEntry::String(_)
5439 | HeapEntry::BigInt(_)
5440 | HeapEntry::Symbol { .. }
5441 | HeapEntry::PrivateName { .. }
5442 | HeapEntry::Iterator { .. }
5443 | HeapEntry::PromiseResolver { .. }
5444 | HeapEntry::PromiseFinally { .. }
5445 | HeapEntry::PromiseAll { .. }
5446 | HeapEntry::AsyncActivation { .. }
5447 | HeapEntry::PromiseAllElement { .. } => None,
5448 }
5449 }
5450
5451 fn own_get(&self, index: usize, key: &PropertyKey) -> Option<Found> {
5454 if let PropertyKey::Named(name) = key {
5455 let slot = |value| self.runtime_slot(value).ok().flatten();
5456 if slot(self.intrinsics.object_prototype) == Some(index) && name.eq_ascii("toString") {
5457 return Some(Found::Value(self.intrinsics.object_to_string()));
5458 }
5459 }
5460 match &self.heap[index] {
5461 HeapEntry::Object { properties, .. }
5462 | HeapEntry::Generator { properties, .. }
5463 | HeapEntry::Script { properties, .. }
5464 | HeapEntry::Date { properties, .. }
5465 | HeapEntry::BuiltinIterator { properties, .. }
5466 | HeapEntry::Collection { properties, .. }
5467 | HeapEntry::Promise { properties, .. }
5468 | HeapEntry::Timeout { properties, .. } => property_lookup(properties, key),
5469 HeapEntry::Array {
5470 elements,
5471 properties,
5472 ..
5473 } => {
5474 if let PropertyKey::Named(name) = key {
5475 if name.eq_ascii("length") {
5476 return Some(Found::Value(number_value(elements.len() as f64)));
5477 }
5478 if let Some(offset) = array_index(name)
5479 && let Some(element) = elements.get(offset as usize)
5480 && *element != Value::HOLE
5481 {
5482 return Some(Found::Value(*element));
5483 }
5484 }
5485 property_lookup(properties, key)
5486 }
5487 HeapEntry::Function {
5488 module,
5489 function,
5490 properties,
5491 ..
5492 } => {
5493 if let Some(found) = property_lookup(properties, key) {
5494 return Some(found);
5495 }
5496 if let PropertyKey::Named(name) = key {
5497 let metadata = &self.module_code(*module).functions()[function.get() as usize];
5498 if name.eq_ascii("length") {
5499 return Some(Found::Value(
5500 number_value(metadata.parameter_count() as f64),
5501 ));
5502 }
5503 if name.eq_ascii("name") {
5504 return Some(Found::Text(
5505 metadata
5506 .name()
5507 .map(|id| self.constant_text(*module, id).clone())
5508 .unwrap_or_default(),
5509 ));
5510 }
5511 }
5512 None
5513 }
5514 HeapEntry::ModuleNamespace { module } => {
5515 let PropertyKey::Named(name) = key else {
5516 return None;
5517 };
5518 match self.namespace_export(*module, name) {
5519 Ok(Some(value)) => Some(Found::Value(value)),
5520 Ok(None) => None,
5521 Err(kind) => Some(Found::Failure(kind)),
5522 }
5523 }
5524 HeapEntry::ExternalModuleNamespace { specifier } => {
5525 let PropertyKey::Named(name) = key else {
5526 return None;
5527 };
5528 let export = self.registry.external[specifier].exports.get(name)?;
5529 Some(Found::Value(export.cell.map_or(export.value, |cell| {
5530 self.registry.cells[cell.0].value
5531 })))
5532 }
5533 HeapEntry::NativeFunction { properties, .. } => property_lookup(properties, key),
5534 HeapEntry::RegExp {
5535 pattern,
5536 flags,
5537 properties,
5538 ..
5539 } => {
5540 if let Some(found) = property_lookup(properties, key) {
5541 return Some(found);
5542 }
5543 if let PropertyKey::Named(name) = key {
5544 let flag = |ascii: &str| {
5545 Found::Value(Value::boolean(
5546 flags.as_units().contains(&u16::from(ascii.as_bytes()[0])),
5547 ))
5548 };
5549 if name.eq_ascii("source") {
5550 return Some(Found::Text(crate::intrinsics::builtins::canonical_source(
5551 pattern,
5552 )));
5553 }
5554 if name.eq_ascii("flags") {
5555 return Some(Found::Text(flags.clone()));
5556 }
5557 if name.eq_ascii("global") {
5558 return Some(flag("g"));
5559 }
5560 if name.eq_ascii("ignoreCase") {
5561 return Some(flag("i"));
5562 }
5563 if name.eq_ascii("multiline") {
5564 return Some(flag("m"));
5565 }
5566 if name.eq_ascii("sticky") {
5567 return Some(flag("y"));
5568 }
5569 if name.eq_ascii("unicode") {
5570 return Some(flag("u"));
5571 }
5572 if name.eq_ascii("dotAll") {
5573 return Some(flag("s"));
5574 }
5575 if name.eq_ascii("lastIndex") {
5576 return Some(Found::Value(Value::int32(0)));
5577 }
5578 }
5579 None
5580 }
5581 HeapEntry::HashState { update, digest, .. } => {
5582 let PropertyKey::Named(name) = key else {
5583 return None;
5584 };
5585 if name.eq_ascii("update") {
5586 Some(Found::Value(*update))
5587 } else if name.eq_ascii("digest") {
5588 Some(Found::Value(*digest))
5589 } else {
5590 None
5591 }
5592 }
5593 HeapEntry::ProcessEnv { .. }
5594 | HeapEntry::String(_)
5595 | HeapEntry::BigInt(_)
5596 | HeapEntry::Symbol { .. }
5597 | HeapEntry::PrivateName { .. }
5598 | HeapEntry::Iterator { .. }
5599 | HeapEntry::PromiseResolver { .. }
5600 | HeapEntry::PromiseFinally { .. }
5601 | HeapEntry::PromiseAll { .. }
5602 | HeapEntry::AsyncActivation { .. }
5603 | HeapEntry::PromiseAllElement { .. } => None,
5604 }
5605 }
5606
5607 fn namespace_export(
5608 &self,
5609 module: ModuleId,
5610 name: &EcmaString,
5611 ) -> Result<Option<Value>, RuntimeErrorKind> {
5612 if module.get() as usize >= self.dynamic_base {
5613 return Ok(None);
5614 }
5615 match self.program().resolve_export(module, name) {
5616 Some(ResolvedExport::Local { module, binding }) => {
5617 let cell = self.registry.modules[module.get() as usize].binding_cells
5618 [binding.get() as usize]
5619 .expect("verified export resolves to a linked cell");
5620 let value = self.registry.cells[cell.0].value;
5621 if value.is_uninitialized() {
5622 Err(RuntimeErrorKind::TemporalDeadZone { module, binding })
5623 } else {
5624 Ok(Some(value))
5625 }
5626 }
5627 Some(ResolvedExport::External { module, edge, name }) => {
5628 let Some(specifier) = self.external_specifier(module, edge) else {
5629 return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5630 };
5631 let name = self.constant_text(module, name);
5632 let Some(export) = self.registry.external[&specifier].exports.get(name) else {
5633 return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5634 };
5635 let Some(cell) = export.cell else {
5636 return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5637 };
5638 Ok(Some(self.registry.cells[cell.0].value))
5639 }
5640 None => Ok(None),
5641 }
5642 }
5643
5644 fn own_data_property(&self, index: usize, name: &str) -> Option<Value> {
5645 let properties = match &self.heap[index] {
5646 HeapEntry::Object { properties, .. }
5647 | HeapEntry::Generator { properties, .. }
5648 | HeapEntry::Script { properties, .. }
5649 | HeapEntry::Array { properties, .. }
5650 | HeapEntry::Function { properties, .. }
5651 | HeapEntry::NativeFunction { properties, .. }
5652 | HeapEntry::RegExp { properties, .. }
5653 | HeapEntry::Date { properties, .. }
5654 | HeapEntry::BuiltinIterator { properties, .. }
5655 | HeapEntry::Collection { properties, .. }
5656 | HeapEntry::Promise { properties, .. }
5657 | HeapEntry::Timeout { properties, .. } => properties,
5658 _ => return None,
5659 };
5660 match properties.get_ascii(name) {
5661 Some(Property::Data { value, .. }) => Some(*value),
5662 _ => None,
5663 }
5664 }
5665
5666 fn prototype_index(&self, index: usize) -> Result<Option<usize>, EvalFailure> {
5667 let prototype = match &self.heap[index] {
5668 HeapEntry::Object { prototype, .. }
5669 | HeapEntry::Generator { prototype, .. }
5670 | HeapEntry::Script { prototype, .. }
5671 | HeapEntry::Array { prototype, .. }
5672 | HeapEntry::Function { prototype, .. }
5673 | HeapEntry::RegExp { prototype, .. }
5674 | HeapEntry::Date { prototype, .. }
5675 | HeapEntry::BuiltinIterator { prototype, .. }
5676 | HeapEntry::Collection { prototype, .. }
5677 | HeapEntry::Promise { prototype, .. }
5678 | HeapEntry::Timeout { prototype, .. }
5679 | HeapEntry::ProcessEnv { prototype, .. } => *prototype,
5680 HeapEntry::NativeFunction { .. } => Some(self.intrinsics.function_prototype),
5681 _ => None,
5682 };
5683 match prototype {
5684 Some(value) => self.runtime_slot(value).map_err(EvalFailure::Runtime),
5685 None => Ok(None),
5686 }
5687 }
5688
5689 pub(crate) fn inherits_from_prototype(
5690 &self,
5691 value: Value,
5692 prototype: Value,
5693 ) -> Result<bool, EvalFailure> {
5694 let Some(mut current) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
5695 return Ok(false);
5696 };
5697 let Some(target) = self.runtime_slot(prototype).map_err(EvalFailure::Runtime)? else {
5698 return Ok(false);
5699 };
5700 let mut traversed = 0;
5701 while let Some(next) = self.prototype_index(current)? {
5702 if next == target {
5703 return Ok(true);
5704 }
5705 current = next;
5706 traversed += 1;
5707 if traversed > self.heap.len() {
5708 return Ok(false);
5709 }
5710 }
5711 Ok(false)
5712 }
5713
5714 fn resolve_set(
5717 &mut self,
5718 object: Value,
5719 key: PropertyKey,
5720 value: Value,
5721 ) -> Result<SetOutcome, EvalFailure> {
5722 match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
5723 Some(index) => {
5724 if matches!(self.heap[index], HeapEntry::ModuleNamespace { .. }) {
5725 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5726 operation: "assign to module namespace",
5727 }));
5728 }
5729 if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5730 let PropertyKey::Named(name) = &key else {
5731 return Ok(SetOutcome::Done);
5732 };
5733 let Ok(name) = name.to_utf8_strict() else {
5734 return Ok(SetOutcome::Done);
5735 };
5736 let text = self.to_string(value)?;
5737 let text = crate::host_objects::env_value_text_lossy(&text);
5738 self.host.set_env(&name, &text);
5739 return Ok(SetOutcome::Done);
5740 }
5741 if let Some(setter) = self.find_setter(index, &key)? {
5742 return Ok(match setter {
5743 Some(setter) => SetOutcome::Setter(setter),
5744 None => SetOutcome::Done,
5745 });
5746 }
5747 self.set_own_data(index, key, value)?;
5748 Ok(SetOutcome::Done)
5749 }
5750 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5751 operation: "set property on primitive",
5752 })),
5753 }
5754 }
5755
5756 fn find_setter(
5757 &self,
5758 index: usize,
5759 key: &PropertyKey,
5760 ) -> Result<Option<Option<Value>>, EvalFailure> {
5761 if self.own_has_non_accessor(index, key) {
5762 return Ok(None);
5763 }
5764 let mut node = index;
5765 let mut guard = 0;
5766 loop {
5767 let accessor = match &self.heap[node] {
5768 HeapEntry::Object { properties, .. }
5769 | HeapEntry::Generator { properties, .. }
5770 | HeapEntry::Script { properties, .. }
5771 | HeapEntry::Array { properties, .. }
5772 | HeapEntry::Function { properties, .. }
5773 | HeapEntry::NativeFunction { properties, .. }
5774 | HeapEntry::RegExp { properties, .. }
5775 | HeapEntry::Date { properties, .. }
5776 | HeapEntry::BuiltinIterator { properties, .. }
5777 | HeapEntry::Collection { properties, .. }
5778 | HeapEntry::Promise { properties, .. }
5779 | HeapEntry::Timeout { properties, .. } => match properties.get(key) {
5780 Some(Property::Accessor { setter, .. }) => Some(Some(*setter)),
5781 Some(Property::Data { .. }) => Some(None),
5782 None => None,
5783 },
5784 _ => None,
5785 };
5786 match accessor {
5787 Some(Some(setter)) => return Ok(Some(setter)),
5788 Some(None) => return Ok(None),
5789 None => {}
5790 }
5791 match self.prototype_index(node)? {
5792 Some(next) => {
5793 node = next;
5794 guard += 1;
5795 if guard > self.heap.len() + 1 {
5796 return Ok(None);
5797 }
5798 }
5799 None => return Ok(None),
5800 }
5801 }
5802 }
5803
5804 fn own_has_non_accessor(&self, index: usize, key: &PropertyKey) -> bool {
5805 match &self.heap[index] {
5806 HeapEntry::Array { elements, .. } => {
5807 if let PropertyKey::Named(name) = key {
5808 if name.eq_ascii("length") {
5809 return true;
5810 }
5811 if let Some(offset) = array_index(name) {
5812 return elements
5813 .get(offset as usize)
5814 .is_some_and(|element| *element != Value::HOLE);
5815 }
5816 }
5817 false
5818 }
5819 HeapEntry::Function { .. } => {
5820 (key.eq_ascii("length") || key.eq_ascii("name"))
5821 && match key {
5822 PropertyKey::Named(name) if name.eq_ascii("length") => {
5823 self.own_data_property(index, "length").is_none()
5824 }
5825 PropertyKey::Named(_) => self.own_data_property(index, "name").is_none(),
5826 _ => false,
5827 }
5828 }
5829 _ => false,
5830 }
5831 }
5832
5833 fn set_own_data(
5834 &mut self,
5835 index: usize,
5836 key: PropertyKey,
5837 value: Value,
5838 ) -> Result<(), EvalFailure> {
5839 if matches!(key, PropertyKey::Named(ref name) if name.eq_ascii("length"))
5840 && matches!(self.heap[index], HeapEntry::Array { .. })
5841 {
5842 let HeapEntry::Array {
5843 elements,
5844 properties,
5845 length_writable,
5846 ..
5847 } = &mut self.heap[index]
5848 else {
5849 unreachable!("array checked above");
5850 };
5851 return array_set_length(
5852 elements,
5853 properties,
5854 *length_writable,
5855 value,
5856 "set array length",
5857 );
5858 }
5859 if let HeapEntry::Array {
5860 elements,
5861 length_writable,
5862 ..
5863 } = &self.heap[index]
5864 && let Some(offset) = key.as_string().and_then(array_index)
5865 && offset as usize >= elements.len()
5866 && !*length_writable
5867 {
5868 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5869 operation: "add index beyond non-writable array length",
5870 }));
5871 }
5872 let (properties, extensible, virtual_exists) = match &self.heap[index] {
5873 HeapEntry::Object {
5874 properties,
5875 extensible,
5876 ..
5877 }
5878 | HeapEntry::Generator {
5879 properties,
5880 extensible,
5881 ..
5882 }
5883 | HeapEntry::Script {
5884 properties,
5885 extensible,
5886 ..
5887 }
5888 | HeapEntry::Function {
5889 properties,
5890 extensible,
5891 ..
5892 }
5893 | HeapEntry::NativeFunction {
5894 properties,
5895 extensible,
5896 ..
5897 }
5898 | HeapEntry::RegExp {
5899 properties,
5900 extensible,
5901 ..
5902 }
5903 | HeapEntry::Date {
5904 properties,
5905 extensible,
5906 ..
5907 }
5908 | HeapEntry::BuiltinIterator {
5909 properties,
5910 extensible,
5911 ..
5912 }
5913 | HeapEntry::Collection {
5914 properties,
5915 extensible,
5916 ..
5917 }
5918 | HeapEntry::Promise {
5919 properties,
5920 extensible,
5921 ..
5922 } => (Some(properties), *extensible, false),
5923 HeapEntry::Array {
5924 elements,
5925 properties,
5926 extensible,
5927 ..
5928 } => {
5929 let virtual_exists = key.as_string().is_some_and(|name| {
5930 name.eq_ascii("length")
5931 || array_index(name).is_some_and(|offset| {
5932 elements
5933 .get(offset as usize)
5934 .is_some_and(|element| *element != Value::HOLE)
5935 })
5936 });
5937 (Some(properties), *extensible, virtual_exists)
5938 }
5939 _ => (None, true, false),
5940 };
5941 if let Some(property) = properties.and_then(|properties| properties.get(&key)) {
5942 match property {
5943 Property::Data {
5944 writable: false, ..
5945 }
5946 | Property::Accessor { .. } => {
5947 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5948 operation: "assign to read only property",
5949 }));
5950 }
5951 Property::Data { writable: true, .. } => {}
5952 }
5953 } else if !extensible && !virtual_exists {
5954 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5955 operation: "add property to non-extensible object",
5956 }));
5957 }
5958
5959 let growth = match &self.heap[index] {
5960 HeapEntry::Object { properties, .. }
5961 | HeapEntry::Generator { properties, .. }
5962 | HeapEntry::Script { properties, .. }
5963 | HeapEntry::Function { properties, .. }
5964 | HeapEntry::NativeFunction { properties, .. }
5965 | HeapEntry::RegExp { properties, .. }
5966 | HeapEntry::Date { properties, .. }
5967 | HeapEntry::BuiltinIterator { properties, .. }
5968 | HeapEntry::Collection { properties, .. }
5969 | HeapEntry::Promise { properties, .. }
5970 | HeapEntry::Timeout { properties, .. } => {
5971 usize::from(!properties.contains_key(&key)) * key.charge_bytes()
5972 }
5973 HeapEntry::Array {
5974 elements,
5975 properties,
5976 ..
5977 } => match &key {
5978 PropertyKey::Named(name) if name.eq_ascii("length") => 0,
5979 PropertyKey::Named(name) => {
5980 if let Some(offset) = array_index(name) {
5981 (offset as usize + 1).saturating_sub(elements.len()) * 8
5982 } else {
5983 usize::from(!properties.contains_key(&key)) * key.charge_bytes()
5984 }
5985 }
5986 PropertyKey::Symbol(_) | PropertyKey::Private(_) => {
5987 usize::from(!properties.contains_key(&key)) * key.charge_bytes()
5988 }
5989 },
5990 HeapEntry::String(_) | HeapEntry::BigInt(_) => {
5991 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5992 operation: "set property on primitive",
5993 }));
5994 }
5995 HeapEntry::Symbol { .. }
5996 | HeapEntry::PrivateName { .. }
5997 | HeapEntry::Iterator { .. }
5998 | HeapEntry::PromiseResolver { .. }
5999 | HeapEntry::PromiseFinally { .. }
6000 | HeapEntry::PromiseAll { .. }
6001 | HeapEntry::AsyncActivation { .. }
6002 | HeapEntry::PromiseAllElement { .. } => {
6003 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6004 operation: "set property on non-object",
6005 }));
6006 }
6007 HeapEntry::ProcessEnv { .. } => {
6008 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6009 operation: "set internal process environment",
6010 }));
6011 }
6012 HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
6013 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6014 operation: "assign to module namespace",
6015 }));
6016 }
6017 HeapEntry::HashState { .. } => {
6018 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6019 operation: "assign to hash state",
6020 }));
6021 }
6022 };
6023 self.charge_heap(growth).map_err(EvalFailure::Runtime)?;
6024 match &mut self.heap[index] {
6025 HeapEntry::Object { properties, .. }
6026 | HeapEntry::Generator { properties, .. }
6027 | HeapEntry::Script { properties, .. }
6028 | HeapEntry::Function { properties, .. }
6029 | HeapEntry::NativeFunction { properties, .. }
6030 | HeapEntry::RegExp { properties, .. }
6031 | HeapEntry::Date { properties, .. }
6032 | HeapEntry::BuiltinIterator { properties, .. }
6033 | HeapEntry::Collection { properties, .. }
6034 | HeapEntry::Promise { properties, .. }
6035 | HeapEntry::Timeout { properties, .. } => {
6036 properties.insert(
6037 key,
6038 Property::Data {
6039 value,
6040 writable: true,
6041 enumerable: true,
6042 configurable: true,
6043 },
6044 );
6045 Ok(())
6046 }
6047 HeapEntry::Array {
6048 elements,
6049 properties,
6050 length_writable,
6051 ..
6052 } => {
6053 match key {
6054 PropertyKey::Named(name) => {
6055 if let Some(offset) = array_index(&name) {
6056 let offset = offset as usize;
6057 if elements.len() <= offset {
6058 array_set_length(
6059 elements,
6060 properties,
6061 *length_writable,
6062 number_value((offset + 1) as f64),
6063 "set array index",
6064 )?;
6065 }
6066 elements[offset] = value;
6067 } else {
6068 properties.insert(
6069 PropertyKey::Named(name),
6070 Property::Data {
6071 value,
6072 writable: true,
6073 enumerable: true,
6074 configurable: true,
6075 },
6076 );
6077 }
6078 }
6079 identity @ (PropertyKey::Symbol(_) | PropertyKey::Private(_)) => {
6080 properties.insert(
6081 identity,
6082 Property::Data {
6083 value,
6084 writable: true,
6085 enumerable: true,
6086 configurable: true,
6087 },
6088 );
6089 }
6090 }
6091 Ok(())
6092 }
6093 HeapEntry::ProcessEnv { .. } => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6094 operation: "set internal process environment",
6095 })),
6096 _ => unreachable!("primitive and identity entries rejected above"),
6097 }
6098 }
6099
6100 fn define_accessor(
6101 &mut self,
6102 object: Value,
6103 key: PropertyKey,
6104 accessor: Value,
6105 kind: AccessorKind,
6106 ) -> Result<(), EvalFailure> {
6107 match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6108 Some(index) => {
6109 self.charge_heap(key.charge_bytes() + 8)
6110 .map_err(EvalFailure::Runtime)?;
6111 let (properties, extensible) = match &mut self.heap[index] {
6112 HeapEntry::Object {
6113 properties,
6114 extensible,
6115 ..
6116 }
6117 | HeapEntry::Generator {
6118 properties,
6119 extensible,
6120 ..
6121 }
6122 | HeapEntry::Script {
6123 properties,
6124 extensible,
6125 ..
6126 }
6127 | HeapEntry::Array {
6128 properties,
6129 extensible,
6130 ..
6131 }
6132 | HeapEntry::Function {
6133 properties,
6134 extensible,
6135 ..
6136 }
6137 | HeapEntry::NativeFunction {
6138 properties,
6139 extensible,
6140 ..
6141 }
6142 | HeapEntry::RegExp {
6143 properties,
6144 extensible,
6145 ..
6146 }
6147 | HeapEntry::Date {
6148 properties,
6149 extensible,
6150 ..
6151 }
6152 | HeapEntry::BuiltinIterator {
6153 properties,
6154 extensible,
6155 ..
6156 }
6157 | HeapEntry::Collection {
6158 properties,
6159 extensible,
6160 ..
6161 }
6162 | HeapEntry::Promise {
6163 properties,
6164 extensible,
6165 ..
6166 } => (properties, *extensible),
6167 _ => {
6168 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6169 operation: "define accessor on primitive",
6170 }));
6171 }
6172 };
6173 if properties
6174 .get(&key)
6175 .is_some_and(|property| !property.configurable())
6176 || (!properties.contains_key(&key) && !extensible)
6177 {
6178 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6179 operation: "define accessor on non-configurable object",
6180 }));
6181 }
6182 let property = properties.get_mut(&key);
6183 match property {
6184 Some(Property::Accessor { getter, setter, .. }) => match kind {
6185 AccessorKind::Getter => *getter = Some(accessor),
6186 AccessorKind::Setter => *setter = Some(accessor),
6187 },
6188 Some(Property::Data { .. }) | None => {
6189 let (getter, setter) = match kind {
6190 AccessorKind::Getter => (Some(accessor), None),
6191 AccessorKind::Setter => (None, Some(accessor)),
6192 };
6193 properties.insert(
6194 key,
6195 Property::Accessor {
6196 getter,
6197 setter,
6198 enumerable: true,
6199 configurable: true,
6200 },
6201 );
6202 }
6203 }
6204 Ok(())
6205 }
6206 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6207 operation: "define accessor on host object",
6208 })),
6209 }
6210 }
6211
6212 fn delete_property(&mut self, object: Value, key: &PropertyKey) -> Result<bool, EvalFailure> {
6213 match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6214 Some(index) => match &mut self.heap[index] {
6215 HeapEntry::Object { properties, .. }
6216 | HeapEntry::Generator { properties, .. }
6217 | HeapEntry::Script { properties, .. }
6218 | HeapEntry::Function { properties, .. }
6219 | HeapEntry::NativeFunction { properties, .. }
6220 | HeapEntry::RegExp { properties, .. }
6221 | HeapEntry::Date { properties, .. }
6222 | HeapEntry::BuiltinIterator { properties, .. }
6223 | HeapEntry::Collection { properties, .. }
6224 | HeapEntry::Promise { properties, .. }
6225 | HeapEntry::Timeout { properties, .. } => {
6226 if properties
6227 .get(key)
6228 .is_some_and(|property| !property.configurable())
6229 {
6230 return Ok(false);
6231 }
6232 properties.remove(key);
6233 Ok(true)
6234 }
6235 HeapEntry::Array {
6236 elements,
6237 properties,
6238 ..
6239 } => {
6240 if properties
6241 .get(key)
6242 .is_some_and(|property| !property.configurable())
6243 {
6244 return Ok(false);
6245 }
6246 if properties.remove(key).is_some() {
6247 return Ok(true);
6248 }
6249 if let PropertyKey::Named(name) = key {
6250 if name.eq_ascii("length") {
6251 return Ok(false);
6252 }
6253 if let Some(offset) = array_index(name) {
6254 if let Some(element) = elements.get_mut(offset as usize) {
6255 *element = Value::HOLE;
6256 }
6257 return Ok(true);
6258 }
6259 }
6260 Ok(true)
6261 }
6262 HeapEntry::ProcessEnv { .. } => {
6263 let PropertyKey::Named(name) = key else {
6264 return Ok(true);
6265 };
6266 Ok(name
6267 .to_utf8_strict()
6268 .is_ok_and(|name| self.host.delete_env(&name)))
6269 }
6270 HeapEntry::String(_)
6271 | HeapEntry::BigInt(_)
6272 | HeapEntry::Symbol { .. }
6273 | HeapEntry::PrivateName { .. }
6274 | HeapEntry::Iterator { .. }
6275 | HeapEntry::PromiseResolver { .. }
6276 | HeapEntry::PromiseFinally { .. }
6277 | HeapEntry::PromiseAll { .. }
6278 | HeapEntry::AsyncActivation { .. }
6279 | HeapEntry::PromiseAllElement { .. }
6280 | HeapEntry::HashState { .. } => Ok(true),
6281 HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
6282 Ok(false)
6283 }
6284 },
6285 None => Ok(true),
6286 }
6287 }
6288
6289 fn has_property(&mut self, object: Value, key: &PropertyKey) -> Result<bool, EvalFailure> {
6290 match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6291 Some(index) => {
6292 if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
6293 let PropertyKey::Named(name) = key else {
6294 return Ok(false);
6295 };
6296 return Ok(name
6297 .to_utf8_strict()
6298 .is_ok_and(|name| self.host.env(&name).is_some()));
6299 }
6300 if matches!(key, PropertyKey::Private(_)) {
6301 return Ok(self.own_get(index, key).is_some());
6302 }
6303 let mut node = index;
6304 let mut guard = 0;
6305 loop {
6306 if self.own_get(node, key).is_some() {
6307 return Ok(true);
6308 }
6309 match self.prototype_index(node)? {
6310 Some(next) => {
6311 node = next;
6312 guard += 1;
6313 if guard > self.heap.len() + 1 {
6314 return Ok(false);
6315 }
6316 }
6317 None => return Ok(false),
6318 }
6319 }
6320 }
6321 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6322 operation: "in",
6323 })),
6324 }
6325 }
6326
6327 pub(crate) fn array_push(&mut self, array: Value, value: Value) -> Result<(), EvalFailure> {
6330 match self.runtime_slot(array).map_err(EvalFailure::Runtime)? {
6331 Some(index) => {
6332 if !matches!(self.heap[index], HeapEntry::Array { .. }) {
6333 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6334 operation: "push on non-array",
6335 }));
6336 }
6337 self.charge_heap(8).map_err(EvalFailure::Runtime)?;
6338 if let HeapEntry::Array {
6339 elements,
6340 properties,
6341 length_writable,
6342 ..
6343 } = &mut self.heap[index]
6344 {
6345 let offset = elements.len();
6346 array_set_length(
6347 elements,
6348 properties,
6349 *length_writable,
6350 number_value((offset + 1) as f64),
6351 "push beyond non-writable array length",
6352 )?;
6353 elements[offset] = value;
6354 }
6355 Ok(())
6356 }
6357 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6358 operation: "push on non-array",
6359 })),
6360 }
6361 }
6362
6363 fn array_extend(&mut self, array: Value, iterable: Value) -> Result<(), EvalFailure> {
6364 let iterator = self.create_iterator(iterable, IteratorKind::Sync)?;
6365 loop {
6366 let (done, value) = self.iterator_next(iterator)?;
6367 if done {
6368 return Ok(());
6369 }
6370 self.array_push(array, value)?;
6371 }
6372 }
6373
6374 fn object_spread(&mut self, target: Value, source: Value) -> Result<(), EvalFailure> {
6375 let target_index = match self.runtime_slot(target).map_err(EvalFailure::Runtime)? {
6376 Some(index)
6377 if matches!(
6378 self.heap[index],
6379 HeapEntry::Object { .. }
6380 | HeapEntry::Generator { .. }
6381 | HeapEntry::Script { .. }
6382 | HeapEntry::Array { .. }
6383 | HeapEntry::Promise { .. }
6384 ) =>
6385 {
6386 index
6387 }
6388 _ => {
6389 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6390 operation: "object spread target is not an object",
6391 }));
6392 }
6393 };
6394 let keys = self.own_property_keys(source)?;
6395 for key in keys {
6396 if !self.own_property_is_enumerable(source, &key)? {
6397 continue;
6398 }
6399 let value = self.get_property_key(source, &key)?;
6400 self.set_own_data(target_index, key, value)?;
6401 }
6402 Ok(())
6403 }
6404
6405 fn set_prototype(&mut self, object: Value, prototype: Value) -> Result<(), EvalFailure> {
6406 let prototype = match self.runtime_slot(prototype).map_err(EvalFailure::Runtime)? {
6407 Some(_) => Some(prototype),
6408 None => match prototype.decode() {
6409 Some(Decoded::Null) => None,
6410 Some(Decoded::HeapRef(_)) => Some(prototype),
6411 _ => {
6412 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6413 operation: "set prototype to non-object",
6414 }));
6415 }
6416 },
6417 };
6418 match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6419 Some(index) => match &mut self.heap[index] {
6420 HeapEntry::Object {
6421 prototype: slot, ..
6422 }
6423 | HeapEntry::Generator {
6424 prototype: slot, ..
6425 }
6426 | HeapEntry::Script {
6427 prototype: slot, ..
6428 }
6429 | HeapEntry::Array {
6430 prototype: slot, ..
6431 }
6432 | HeapEntry::Function {
6433 prototype: slot, ..
6434 }
6435 | HeapEntry::RegExp {
6436 prototype: slot, ..
6437 }
6438 | HeapEntry::Date {
6439 prototype: slot, ..
6440 }
6441 | HeapEntry::BuiltinIterator {
6442 prototype: slot, ..
6443 }
6444 | HeapEntry::Collection {
6445 prototype: slot, ..
6446 }
6447 | HeapEntry::Promise {
6448 prototype: slot, ..
6449 } => {
6450 *slot = prototype;
6451 Ok(())
6452 }
6453 _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6454 operation: "set prototype on primitive",
6455 })),
6456 },
6457 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6458 operation: "set prototype on host object",
6459 })),
6460 }
6461 }
6462
6463 pub(crate) fn create_generator(
6464 &mut self,
6465 start: GeneratorStart,
6466 ) -> Result<Value, RuntimeErrorKind> {
6467 self.allocate(HeapEntry::Generator {
6468 state: GeneratorState::SuspendedStart(start),
6469 properties: PropertyMap::default(),
6470 prototype: Some(self.intrinsics.builtins.generator_prototype()),
6471 extensible: true,
6472 })
6473 }
6474
6475 fn resume_generator(
6476 &mut self,
6477 generator: Value,
6478 resume_value: Value,
6479 ) -> Result<Value, EvalFailure> {
6480 let state = self.take_generator_state(generator)?;
6481 if matches!(&state, GeneratorState::Completed) {
6482 return self.iterator_result(Value::UNDEFINED, true);
6483 }
6484
6485 let stop_depth = self.frames.len();
6486 let return_to = self.frames.last().map(|frame| ReturnTo {
6487 destination: None,
6488 call_pc: frame.pc,
6489 constructed: None,
6490 });
6491 let prepared = match state {
6492 GeneratorState::SuspendedStart(start) => self
6493 .push_frame(
6494 start.target,
6495 &start.captures,
6496 start.this_value,
6497 start.new_target,
6498 &start.args,
6499 return_to,
6500 )
6501 .map_err(|error| EvalFailure::Runtime(error.kind)),
6502 GeneratorState::Suspended(activation) => {
6503 self.push_resumed_generator_frame(activation, resume_value, return_to)
6504 }
6505 GeneratorState::Executing | GeneratorState::Completed => unreachable!(),
6506 };
6507 if let Err(failure) = prepared {
6508 self.settle_generator_completed(generator)?;
6509 return Err(failure);
6510 }
6511
6512 let resumed = self.run_generator_activation(stop_depth);
6513 match resumed {
6514 Ok(GeneratorResume::Yield { value, activation }) => {
6515 self.settle_generator_yield(generator, value, activation)
6516 }
6517 Ok(GeneratorResume::Return(value)) => {
6518 self.settle_generator_completed(generator)?;
6519 self.iterator_result(value, true)
6520 }
6521 Ok(GeneratorResume::Throw { value, origin }) => {
6522 self.settle_generator_completed(generator)?;
6523 Err(EvalFailure::ThrowValueOrigin { value, origin })
6524 }
6525 Err(failure) => {
6526 self.settle_generator_completed(generator)?;
6527 Err(failure)
6528 }
6529 }
6530 }
6531
6532 fn push_resumed_generator_frame(
6533 &mut self,
6534 activation: SuspendedActivation,
6535 resume_value: Value,
6536 return_to: Option<ReturnTo>,
6537 ) -> Result<(), EvalFailure> {
6538 if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
6539 self.release_suspended_activation_registers(activation.registers.len());
6540 return Err(EvalFailure::Runtime(RuntimeErrorKind::CallDepthExceeded {
6541 limit: self.limits.max_call_depth,
6542 }));
6543 }
6544 let suspend_pc = activation
6545 .resume_token
6546 .checked_sub(1)
6547 .expect("suspended generator token is nonzero") as usize;
6548 let instruction = self.module_code(activation.target.module).functions()
6549 [activation.target.function.get() as usize]
6550 .code()[suspend_pc];
6551 let Instruction::Suspend { dst, resume, .. } = instruction else {
6552 unreachable!("generator resume token names a suspend instruction");
6553 };
6554 let mut frame = Frame {
6555 module: activation.target.module,
6556 function: activation.target.function.get() as usize,
6557 pc: resume.get() as usize,
6558 registers: activation.registers,
6559 return_to,
6560 this_value: activation.this_value,
6561 new_target: activation.new_target,
6562 args: activation.args,
6563 arguments_object: activation.arguments_object,
6564 };
6565 frame.registers[dst.get() as usize] = resume_value;
6566 self.frames.push(frame);
6567 Ok(())
6568 }
6569
6570 fn run_generator_activation(
6571 &mut self,
6572 stop_depth: usize,
6573 ) -> Result<GeneratorResume, EvalFailure> {
6574 self.last_completion = None;
6575 self.pending_generator_resume = None;
6576 self.callback_boundaries.push(stop_depth);
6577 self.generator_boundaries.push(stop_depth);
6578 let result = self.run_loop(stop_depth);
6579 self.generator_boundaries
6580 .pop()
6581 .expect("generator execution owns its suspend boundary");
6582 self.callback_boundaries
6583 .pop()
6584 .expect("generator execution owns its unwind boundary");
6585
6586 match result {
6587 Ok(Some(execution)) => Ok(GeneratorResume::Return(execution.value)),
6588 Ok(None) => {
6589 if let Some(resume) = self.pending_generator_resume.take() {
6590 return Ok(resume);
6591 }
6592 let value = self.last_completion.take().unwrap_or(Value::UNDEFINED);
6593 Ok(GeneratorResume::Return(value))
6594 }
6595 Err(error) => {
6596 self.unwind_frames_to(stop_depth);
6597 match error.kind {
6598 RuntimeErrorKind::UncaughtThrow { value, origin } => {
6599 Ok(GeneratorResume::Throw { value, origin })
6600 }
6601 kind => Err(EvalFailure::Runtime(kind)),
6602 }
6603 }
6604 }
6605 }
6606
6607 pub(crate) fn take_generator_state(
6608 &mut self,
6609 generator: Value,
6610 ) -> Result<GeneratorState, EvalFailure> {
6611 let Some(index) = self.runtime_slot(generator).map_err(EvalFailure::Runtime)? else {
6612 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6613 operation: "Generator.prototype.next called on incompatible receiver",
6614 }));
6615 };
6616 let HeapEntry::Generator { state, .. } = &mut self.heap[index] else {
6617 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6618 operation: "Generator.prototype.next called on incompatible receiver",
6619 }));
6620 };
6621 match std::mem::replace(state, GeneratorState::Executing) {
6622 GeneratorState::Executing => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6623 operation: "generator is already running",
6624 })),
6625 GeneratorState::Completed => {
6626 *state = GeneratorState::Completed;
6627 Ok(GeneratorState::Completed)
6628 }
6629 state => Ok(state),
6630 }
6631 }
6632
6633 pub(crate) fn settle_generator_yield(
6634 &mut self,
6635 generator: Value,
6636 value: Value,
6637 activation: SuspendedActivation,
6638 ) -> Result<Value, EvalFailure> {
6639 let register_count = activation.registers.len();
6640 let result = match self.iterator_result(value, false) {
6641 Ok(result) => result,
6642 Err(failure) => {
6643 self.release_suspended_activation_registers(register_count);
6644 self.replace_executing_generator(generator, GeneratorState::Completed)?;
6645 return Err(failure);
6646 }
6647 };
6648 if let Err(failure) =
6649 self.replace_executing_generator(generator, GeneratorState::Suspended(activation))
6650 {
6651 self.release_suspended_activation_registers(register_count);
6652 return Err(failure);
6653 }
6654 Ok(result)
6655 }
6656
6657 pub(crate) fn settle_generator_completed(
6658 &mut self,
6659 generator: Value,
6660 ) -> Result<(), EvalFailure> {
6661 self.replace_executing_generator(generator, GeneratorState::Completed)
6662 }
6663
6664 fn replace_executing_generator(
6665 &mut self,
6666 generator: Value,
6667 next: GeneratorState,
6668 ) -> Result<(), EvalFailure> {
6669 let Some(index) = self.runtime_slot(generator).map_err(EvalFailure::Runtime)? else {
6670 return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6671 value: generator,
6672 }));
6673 };
6674 let HeapEntry::Generator { state, .. } = &mut self.heap[index] else {
6675 return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6676 value: generator,
6677 }));
6678 };
6679 if !matches!(state, GeneratorState::Executing) {
6680 return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6681 value: generator,
6682 }));
6683 }
6684 *state = next;
6685 Ok(())
6686 }
6687
6688 pub(crate) fn start_async_call(
6693 &mut self,
6694 target: RuntimeFunction,
6695 captures: &[Value],
6696 this_value: Value,
6697 new_target: Value,
6698 arguments: &[Value],
6699 ) -> Result<Value, EvalFailure> {
6700 let promise = self.create_promise()?;
6701 let record = self.create_async_activation(promise)?;
6702 let stop_depth = self.frames.len();
6703 let return_to = self.frames.last().map(|frame| ReturnTo {
6704 destination: None,
6705 call_pc: frame.pc,
6706 constructed: None,
6707 });
6708 self.push_frame(
6709 target, captures, this_value, new_target, arguments, return_to,
6710 )
6711 .map_err(|error| EvalFailure::Runtime(error.kind))?;
6712 let step = self.drive_async_activation(stop_depth, None);
6713 self.settle_async_step(record, promise, step)?;
6714 Ok(promise)
6715 }
6716
6717 fn resume_async(
6722 &mut self,
6723 record: Value,
6724 value: Value,
6725 rejection: Option<ThrowOrigin>,
6726 ) -> Result<(), RuntimeErrorKind> {
6727 let promise = self.async_activation_promise(record)?;
6728 let activation = self.take_async_activation(record)?;
6729 let register_count = activation.registers.len();
6730 if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
6731 self.release_suspended_activation_registers(register_count);
6732 return Err(RuntimeErrorKind::CallDepthExceeded {
6733 limit: self.limits.max_call_depth,
6734 });
6735 }
6736 let suspend_pc = activation
6737 .resume_token
6738 .checked_sub(1)
6739 .expect("suspended async token is nonzero") as usize;
6740 let instruction = self.module_code(activation.target.module).functions()
6741 [activation.target.function.get() as usize]
6742 .code()[suspend_pc];
6743 let Instruction::Suspend { dst, resume, .. } = instruction else {
6744 unreachable!("async resume token names a suspend instruction");
6745 };
6746 let stop_depth = self.frames.len();
6747 let return_to = self.frames.last().map(|frame| ReturnTo {
6748 destination: None,
6749 call_pc: frame.pc,
6750 constructed: None,
6751 });
6752 let mut frame = Frame {
6753 module: activation.target.module,
6754 function: activation.target.function.get() as usize,
6755 pc: resume.get() as usize,
6756 registers: activation.registers,
6757 return_to,
6758 this_value: activation.this_value,
6759 new_target: activation.new_target,
6760 args: activation.args,
6761 arguments_object: activation.arguments_object,
6762 };
6763 let inject = match rejection {
6764 None => {
6765 frame.registers[dst.get() as usize] = value;
6766 None
6767 }
6768 Some(origin) => Some((value, origin, suspend_pc)),
6769 };
6770 self.frames.push(frame);
6771 let step = self.drive_async_activation(stop_depth, inject);
6772 match self.settle_async_step(record, promise, step) {
6773 Ok(()) => Ok(()),
6774 Err(EvalFailure::Runtime(kind)) => Err(kind),
6775 Err(_) => Err(RuntimeErrorKind::InvalidValue { value: record }),
6776 }
6777 }
6778
6779 fn drive_async_activation(
6785 &mut self,
6786 stop_depth: usize,
6787 inject: Option<(Value, ThrowOrigin, usize)>,
6788 ) -> Result<AsyncStep, EvalFailure> {
6789 self.last_completion = None;
6790 self.pending_async_suspend = None;
6791 self.callback_boundaries.push(stop_depth);
6792 self.async_boundaries.push(stop_depth);
6793 let result = match inject {
6794 None => self.run_loop(stop_depth),
6795 Some((value, origin, faulting_pc)) => match self.throw(value, origin, faulting_pc) {
6796 Ok(()) => self.run_loop(stop_depth),
6797 Err(error) => Err(error),
6798 },
6799 };
6800 self.async_boundaries
6801 .pop()
6802 .expect("async execution owns its suspend boundary");
6803 self.callback_boundaries
6804 .pop()
6805 .expect("async execution owns its unwind boundary");
6806 match result {
6807 Ok(Some(execution)) => Ok(AsyncStep::Return(execution.value)),
6808 Ok(None) => {
6809 if let Some((awaited, activation)) = self.pending_async_suspend.take() {
6810 Ok(AsyncStep::Suspend {
6811 awaited,
6812 activation,
6813 })
6814 } else {
6815 Ok(AsyncStep::Return(
6816 self.last_completion.take().unwrap_or(Value::UNDEFINED),
6817 ))
6818 }
6819 }
6820 Err(error) => {
6821 self.unwind_frames_to(stop_depth);
6822 match error.kind {
6823 RuntimeErrorKind::UncaughtThrow { value, origin } => {
6824 Ok(AsyncStep::Throw { value, origin })
6825 }
6826 kind => Err(EvalFailure::Runtime(kind)),
6827 }
6828 }
6829 }
6830 }
6831
6832 fn settle_async_step(
6834 &mut self,
6835 record: Value,
6836 promise: Value,
6837 step: Result<AsyncStep, EvalFailure>,
6838 ) -> Result<(), EvalFailure> {
6839 match step {
6840 Ok(AsyncStep::Suspend {
6841 awaited,
6842 activation,
6843 }) => {
6844 let register_count = activation.registers.len();
6845 let result = self
6846 .store_async_activation(record, activation)
6847 .and_then(|()| self.await_promise(awaited, record));
6848 if result.is_err() {
6849 let released = self
6850 .take_async_activation(record)
6851 .map_or(register_count, |stored| stored.registers.len());
6852 self.release_suspended_activation_registers(released);
6853 }
6854 result
6855 }
6856 Ok(AsyncStep::Return(value)) => self
6857 .resolve_promise(promise, value)
6858 .map_err(EvalFailure::Runtime),
6859 Ok(AsyncStep::Throw { value, origin }) => self
6860 .reject_promise(promise, value, origin)
6861 .map_err(EvalFailure::Runtime),
6862 Err(failure) => Err(failure),
6863 }
6864 }
6865
6866 fn await_promise(&mut self, awaited: Value, record: Value) -> Result<(), EvalFailure> {
6870 let promise = self.promise_resolve(awaited)?;
6871 let index = self
6872 .runtime_slot(promise)
6873 .map_err(EvalFailure::Runtime)?
6874 .ok_or(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6875 value: promise,
6876 }))?;
6877 let settled = match &self.heap[index] {
6878 HeapEntry::Promise {
6879 state: PromiseState::Pending { .. },
6880 ..
6881 } => None,
6882 HeapEntry::Promise {
6883 state: PromiseState::Fulfilled { value },
6884 ..
6885 } => Some((true, *value, ThrowOrigin::Bytecode)),
6886 HeapEntry::Promise {
6887 state: PromiseState::Rejected { reason, origin },
6888 ..
6889 } => Some((false, *reason, *origin)),
6890 _ => {
6891 return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6892 value: promise,
6893 }));
6894 }
6895 };
6896 if let Some((fulfilled, value, origin)) = settled {
6897 self.ensure_microtask_capacity(1)
6898 .map_err(EvalFailure::Runtime)?;
6899 let reaction = if fulfilled {
6900 PromiseReaction::AsyncFulfill { activation: record }
6901 } else {
6902 PromiseReaction::AsyncReject { activation: record }
6903 };
6904 self.microtasks.push_back(MicrotaskJob::Reaction {
6905 reaction,
6906 value,
6907 origin,
6908 });
6909 return Ok(());
6910 }
6911 self.charge_promise_reactions(2)?;
6912 let HeapEntry::Promise {
6913 state:
6914 PromiseState::Pending {
6915 fulfill_reactions,
6916 reject_reactions,
6917 },
6918 ..
6919 } = &mut self.heap[index]
6920 else {
6921 unreachable!("pending Promise state was checked before reaction registration");
6922 };
6923 fulfill_reactions.push(PromiseReaction::AsyncFulfill { activation: record });
6924 reject_reactions.push(PromiseReaction::AsyncReject { activation: record });
6925 Ok(())
6926 }
6927
6928 fn create_async_activation(&mut self, promise: Value) -> Result<Value, EvalFailure> {
6929 self.allocate(HeapEntry::AsyncActivation {
6930 activation: None,
6931 promise,
6932 })
6933 .map_err(EvalFailure::Runtime)
6934 }
6935
6936 fn store_async_activation(
6937 &mut self,
6938 record: Value,
6939 activation: SuspendedActivation,
6940 ) -> Result<(), EvalFailure> {
6941 let index = self
6942 .runtime_slot(record)
6943 .map_err(EvalFailure::Runtime)?
6944 .ok_or(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6945 value: record,
6946 }))?;
6947 let HeapEntry::AsyncActivation {
6948 activation: slot, ..
6949 } = &mut self.heap[index]
6950 else {
6951 return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6952 value: record,
6953 }));
6954 };
6955 *slot = Some(activation);
6956 Ok(())
6957 }
6958
6959 fn take_async_activation(
6962 &mut self,
6963 record: Value,
6964 ) -> Result<SuspendedActivation, RuntimeErrorKind> {
6965 let index = self
6966 .runtime_slot(record)?
6967 .ok_or(RuntimeErrorKind::InvalidValue { value: record })?;
6968 let HeapEntry::AsyncActivation {
6969 activation: slot, ..
6970 } = &mut self.heap[index]
6971 else {
6972 return Err(RuntimeErrorKind::InvalidValue { value: record });
6973 };
6974 slot.take()
6975 .ok_or(RuntimeErrorKind::InvalidValue { value: record })
6976 }
6977
6978 fn async_activation_promise(&self, record: Value) -> Result<Value, RuntimeErrorKind> {
6979 let index = self
6980 .runtime_slot(record)?
6981 .ok_or(RuntimeErrorKind::InvalidValue { value: record })?;
6982 let HeapEntry::AsyncActivation { promise, .. } = &self.heap[index] else {
6983 return Err(RuntimeErrorKind::InvalidValue { value: record });
6984 };
6985 Ok(*promise)
6986 }
6987
6988 pub(crate) fn iterator_result(
6989 &mut self,
6990 value: Value,
6991 done: bool,
6992 ) -> Result<Value, EvalFailure> {
6993 let result = self
6994 .allocate(HeapEntry::Object {
6995 properties: PropertyMap::default(),
6996 prototype: Some(self.intrinsics.object_prototype),
6997 boxed_primitive: None,
6998 extensible: true,
6999 })
7000 .map_err(EvalFailure::Runtime)?;
7001 self.set_data_property(result, "value", value)?;
7002 self.set_data_property(result, "done", Value::boolean(done))?;
7003 Ok(result)
7004 }
7005
7006 fn create_iterator(&mut self, src: Value, kind: IteratorKind) -> Result<Value, EvalFailure> {
7009 if kind == IteratorKind::Keys {
7010 let keys = self.enumerable_keys(src)?;
7011 return self
7012 .allocate(HeapEntry::Iterator {
7013 state: IteratorState::Keys { index: 0, keys },
7014 })
7015 .map_err(EvalFailure::Runtime);
7016 }
7017
7018 let iterator_symbol = self.intrinsics.builtins.symbol_iterator();
7019 let iterator_key = self.to_property_key(iterator_symbol)?;
7020 let method = self.get_property_key(src, &iterator_key)?;
7021 if !self.is_callable(method)? {
7022 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7023 operation: "value is not iterable",
7024 }));
7025 }
7026 let iterator = self.call_value(method, src, &[])?;
7027 if !self.is_object(iterator) {
7028 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7029 operation: "iterator method returned a non-object",
7030 }));
7031 }
7032 let next = self.get_named_property(iterator, "next")?;
7033 self.create_protocol_iterator(iterator, next)
7034 }
7035
7036 pub(crate) fn create_protocol_iterator(
7037 &mut self,
7038 iterator: Value,
7039 next: Value,
7040 ) -> Result<Value, EvalFailure> {
7041 self.allocate(HeapEntry::Iterator {
7042 state: IteratorState::Protocol { iterator, next },
7043 })
7044 .map_err(EvalFailure::Runtime)
7045 }
7046
7047 fn own_property_keys(&self, src: Value) -> Result<Vec<PropertyKey>, EvalFailure> {
7048 match self.runtime_slot(src).map_err(EvalFailure::Runtime)? {
7049 Some(index) => match &self.heap[index] {
7050 HeapEntry::Object { properties, .. }
7051 | HeapEntry::Generator { properties, .. }
7052 | HeapEntry::Script { properties, .. }
7053 | HeapEntry::Function { properties, .. }
7054 | HeapEntry::NativeFunction { properties, .. }
7055 | HeapEntry::RegExp { properties, .. }
7056 | HeapEntry::Date { properties, .. }
7057 | HeapEntry::BuiltinIterator { properties, .. }
7058 | HeapEntry::Collection { properties, .. }
7059 | HeapEntry::Promise { properties, .. }
7060 | HeapEntry::Timeout { properties, .. } => Ok(ordered_property_keys(properties)),
7061 HeapEntry::Array {
7062 elements,
7063 properties,
7064 ..
7065 } => {
7066 let mut indices: Vec<(usize, PropertyKey)> = elements
7067 .iter()
7068 .enumerate()
7069 .filter(|(_, element)| **element != Value::HOLE)
7070 .map(|(offset, _)| {
7071 (
7072 offset,
7073 PropertyKey::Named(EcmaString::from_utf8(&offset.to_string())),
7074 )
7075 })
7076 .collect();
7077 let mut suffix = Vec::new();
7078 for key in ordered_property_keys(properties) {
7079 let Some(offset) = key.as_string().and_then(array_index) else {
7080 suffix.push(key);
7081 continue;
7082 };
7083 let offset = offset as usize;
7084 if elements
7085 .get(offset)
7086 .is_some_and(|element| *element != Value::HOLE)
7087 {
7088 continue;
7089 }
7090 indices.push((offset, key));
7091 }
7092 indices.sort_unstable_by_key(|(offset, _)| *offset);
7093 Ok(indices
7094 .into_iter()
7095 .map(|(_, key)| key)
7096 .chain(suffix)
7097 .collect())
7098 }
7099 HeapEntry::String(text) => Ok((0..text.len_units())
7100 .map(|index| PropertyKey::Named(EcmaString::from_utf8(&index.to_string())))
7101 .collect()),
7102 HeapEntry::ModuleNamespace { module } => {
7103 let mut names: Vec<EcmaString> = self
7104 .program_module(*module)
7105 .exports
7106 .iter()
7107 .map(|export| self.constant_text(*module, export.name).clone())
7108 .collect();
7109 names.sort();
7110 Ok(names.into_iter().map(PropertyKey::Named).collect())
7111 }
7112 HeapEntry::ExternalModuleNamespace { specifier } => Ok(self.registry.external
7113 [specifier]
7114 .exports
7115 .keys()
7116 .cloned()
7117 .map(PropertyKey::Named)
7118 .collect()),
7119 HeapEntry::ProcessEnv { .. }
7120 | HeapEntry::BigInt(_)
7121 | HeapEntry::Symbol { .. }
7122 | HeapEntry::PrivateName { .. }
7123 | HeapEntry::HashState { .. }
7124 | HeapEntry::Iterator { .. }
7125 | HeapEntry::PromiseResolver { .. }
7126 | HeapEntry::PromiseFinally { .. }
7127 | HeapEntry::PromiseAll { .. }
7128 | HeapEntry::AsyncActivation { .. }
7129 | HeapEntry::PromiseAllElement { .. } => Ok(Vec::new()),
7130 },
7131 None => Ok(Vec::new()),
7132 }
7133 }
7134
7135 fn own_property_is_enumerable(
7136 &self,
7137 src: Value,
7138 key: &PropertyKey,
7139 ) -> Result<bool, EvalFailure> {
7140 let Some(index) = self.runtime_slot(src).map_err(EvalFailure::Runtime)? else {
7141 return Ok(false);
7142 };
7143 Ok(match &self.heap[index] {
7144 HeapEntry::Array {
7145 elements,
7146 properties,
7147 ..
7148 } => properties.get(key).map_or_else(
7149 || {
7150 key.as_string().is_some_and(|name| {
7151 array_index(name).is_some_and(|offset| {
7152 elements
7153 .get(offset as usize)
7154 .is_some_and(|element| *element != Value::HOLE)
7155 })
7156 })
7157 },
7158 Property::enumerable,
7159 ),
7160 HeapEntry::String(text) => key.as_string().is_some_and(|name| {
7161 array_index(name).is_some_and(|offset| (offset as usize) < text.len_units())
7162 }),
7163 HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
7164 matches!(key, PropertyKey::Named(_))
7165 }
7166 HeapEntry::Object { properties, .. }
7167 | HeapEntry::Generator { properties, .. }
7168 | HeapEntry::Script { properties, .. }
7169 | HeapEntry::Function { properties, .. }
7170 | HeapEntry::NativeFunction { properties, .. }
7171 | HeapEntry::RegExp { properties, .. }
7172 | HeapEntry::Date { properties, .. }
7173 | HeapEntry::BuiltinIterator { properties, .. }
7174 | HeapEntry::Collection { properties, .. }
7175 | HeapEntry::Promise { properties, .. }
7176 | HeapEntry::Timeout { properties, .. } => {
7177 properties.get(key).is_some_and(Property::enumerable)
7178 }
7179 _ => false,
7180 })
7181 }
7182
7183 fn enumerable_keys(&self, src: Value) -> Result<Vec<EcmaString>, EvalFailure> {
7184 let mut names = Vec::new();
7185 for key in self.own_property_keys(src)? {
7186 if !self.own_property_is_enumerable(src, &key)? {
7187 continue;
7188 }
7189 if let PropertyKey::Named(name) = key {
7190 names.push(name);
7191 }
7192 }
7193 Ok(names)
7194 }
7195
7196 fn iterator_next(&mut self, iterator: Value) -> Result<(bool, Value), EvalFailure> {
7197 let (callee, this_value) = match self.prepare_iterator_next(iterator)? {
7198 IteratorNextPrepared::Ready { done, value } => return Ok((done, value)),
7199 IteratorNextPrepared::Call { callee, this_value } => (callee, this_value),
7200 };
7201
7202 let result = self.call_value(callee, this_value, &[])?;
7203 if !self.is_object(result) {
7204 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7205 operation: "iterator next returned a non-object",
7206 }));
7207 }
7208 let done = self.get_named_property(result, "done")?;
7209 if self.truthy(done) {
7210 return Ok((true, Value::UNDEFINED));
7211 }
7212 let value = self.get_named_property(result, "value")?;
7213 Ok((false, value))
7214 }
7215
7216 pub(crate) fn prepare_iterator_next(
7217 &mut self,
7218 iterator: Value,
7219 ) -> Result<IteratorNextPrepared, EvalFailure> {
7220 let iterator_index = self
7221 .runtime_slot(iterator)
7222 .map_err(EvalFailure::Runtime)?
7223 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
7224 operation: "iterator next on non-iterator",
7225 }))?;
7226 match &self.heap[iterator_index] {
7227 HeapEntry::Iterator {
7228 state: IteratorState::Keys { index, keys },
7229 } => {
7230 let Some(text) = keys.get(*index).cloned() else {
7231 return Ok(IteratorNextPrepared::Ready {
7232 done: true,
7233 value: Value::UNDEFINED,
7234 });
7235 };
7236 let value = self
7237 .allocate(HeapEntry::String(text))
7238 .map_err(EvalFailure::Runtime)?;
7239 self.advance_iterator(iterator_index);
7240 Ok(IteratorNextPrepared::Ready { done: false, value })
7241 }
7242 HeapEntry::Iterator {
7243 state: IteratorState::Protocol { iterator, next },
7244 } => Ok(IteratorNextPrepared::Call {
7245 callee: *next,
7246 this_value: *iterator,
7247 }),
7248 _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7249 operation: "iterator next on non-iterator",
7250 })),
7251 }
7252 }
7253
7254 pub(crate) fn iterable_values(&mut self, source: Value) -> Result<Vec<Value>, EvalFailure> {
7255 let iterator = self.create_iterator(source, IteratorKind::Sync)?;
7256 let mut values = Vec::new();
7257 loop {
7258 let (done, value) = self.iterator_next(iterator)?;
7259 if done {
7260 return Ok(values);
7261 }
7262 let bytes = values
7263 .len()
7264 .checked_add(1)
7265 .and_then(|length| length.checked_mul(std::mem::size_of::<Value>()))
7266 .ok_or(EvalFailure::Runtime(
7267 RuntimeErrorKind::HeapByteLimitExceeded {
7268 limit: self.limits.max_heap_bytes,
7269 },
7270 ))?;
7271 self.ensure_allocation_capacity(1, bytes)
7272 .map_err(EvalFailure::Runtime)?;
7273 values.push(value);
7274 }
7275 }
7276
7277 fn advance_iterator(&mut self, iterator_index: usize) {
7278 if let HeapEntry::Iterator {
7279 state: IteratorState::Keys { index, .. },
7280 } = &mut self.heap[iterator_index]
7281 {
7282 *index += 1;
7283 }
7284 }
7285
7286 fn eval_unary(&mut self, op: UnaryOp, operand: Value) -> Result<Value, EvalFailure> {
7289 match op {
7290 UnaryOp::Void => Ok(Value::UNDEFINED),
7291 UnaryOp::TypeOf => {
7292 let text = EcmaString::from_utf8(self.type_of(operand));
7293 self.allocate(HeapEntry::String(text))
7294 .map_err(EvalFailure::Runtime)
7295 }
7296 UnaryOp::Plus => self.to_number(operand),
7297 UnaryOp::Negate => {
7298 if let Some(text) = self.bigint_text(operand) {
7299 let negated = if text == "0" {
7300 "0".to_owned()
7301 } else if let Some(positive) = text.strip_prefix('-') {
7302 positive.to_owned()
7303 } else {
7304 format!("-{text}")
7305 };
7306 return self
7307 .allocate(HeapEntry::BigInt(negated))
7308 .map_err(EvalFailure::Runtime);
7309 }
7310 let number =
7311 numeric_f64(self.to_number(operand)?).expect("ToNumber returns numeric");
7312 Ok(number_value(-number))
7313 }
7314 UnaryOp::BitwiseNot => {
7315 if let Some(text) = self.bigint_text(operand) {
7316 let value = text.parse::<i128>().map_err(|_| {
7317 EvalFailure::Throw(ThrowOrigin::RangeError {
7318 operation: "bigint bitwise not",
7319 })
7320 })?;
7321 return self
7322 .allocate(HeapEntry::BigInt((!value).to_string()))
7323 .map_err(EvalFailure::Runtime);
7324 }
7325 Ok(Value::int32(
7326 (!to_int32(numeric_f64(self.to_number(operand)?).unwrap())) as u32,
7327 ))
7328 }
7329 UnaryOp::LogicalNot => Ok(Value::boolean(!self.truthy(operand))),
7330 }
7331 }
7332
7333 fn eval_binary(
7334 &mut self,
7335 op: BinaryOp,
7336 left: Value,
7337 right: Value,
7338 ) -> Result<Value, EvalFailure> {
7339 match op {
7340 BinaryOp::StrictEqual => Ok(Value::boolean(self.strict_equal(left, right))),
7341 BinaryOp::StrictNotEqual => Ok(Value::boolean(!self.strict_equal(left, right))),
7342 BinaryOp::Equal | BinaryOp::NotEqual => {
7343 let equal = self.abstract_equal(left, right)?;
7344 Ok(Value::boolean(if op == BinaryOp::Equal {
7345 equal
7346 } else {
7347 !equal
7348 }))
7349 }
7350 BinaryOp::LessThan
7351 | BinaryOp::LessThanOrEqual
7352 | BinaryOp::GreaterThan
7353 | BinaryOp::GreaterThanOrEqual => {
7354 let ordering = self.relational_compare(left, right)?;
7355 let result = match (op, ordering) {
7356 (_, None) => false,
7357 (BinaryOp::LessThan, Some(order)) => order == Ordering::Less,
7358 (BinaryOp::LessThanOrEqual, Some(order)) => order != Ordering::Greater,
7359 (BinaryOp::GreaterThan, Some(order)) => order == Ordering::Greater,
7360 (BinaryOp::GreaterThanOrEqual, Some(order)) => order != Ordering::Less,
7361 _ => unreachable!(),
7362 };
7363 Ok(Value::boolean(result))
7364 }
7365 BinaryOp::InstanceOf => self.instance_of(left, right).map(Value::boolean),
7366 BinaryOp::In => {
7367 let key = self.to_property_key(left)?;
7368 self.has_property(right, &key).map(Value::boolean)
7369 }
7370 BinaryOp::Add => self.add(left, right),
7371 BinaryOp::Subtract
7372 | BinaryOp::Multiply
7373 | BinaryOp::Divide
7374 | BinaryOp::Remainder
7375 | BinaryOp::Exponent
7376 | BinaryOp::BitAnd
7377 | BinaryOp::BitOr
7378 | BinaryOp::BitXor
7379 | BinaryOp::ShiftLeft
7380 | BinaryOp::ShiftRight
7381 | BinaryOp::UnsignedShiftRight => self.numeric_binary(op, left, right),
7382 }
7383 }
7384
7385 fn add(&mut self, left: Value, right: Value) -> Result<Value, EvalFailure> {
7386 let left = self.to_primitive_default(left)?;
7387 let right = self.to_primitive_default(right)?;
7388 let left_string = self.string_text(left).cloned();
7389 let right_string = self.string_text(right).cloned();
7390 if left_string.is_some() || right_string.is_some() {
7391 let left = match left_string {
7392 Some(text) => text,
7393 None => self.to_string(left)?,
7394 };
7395 let right = match right_string {
7396 Some(text) => text,
7397 None => self.to_string(right)?,
7398 };
7399 let mut builder = EcmaStringBuilder::with_capacity(
7400 left.len_units().saturating_add(right.len_units()),
7401 );
7402 for &unit in left.as_units() {
7403 builder.push_unit(unit);
7404 }
7405 for &unit in right.as_units() {
7406 builder.push_unit(unit);
7407 }
7408 return self
7409 .allocate(HeapEntry::String(builder.finish()))
7410 .map_err(EvalFailure::Runtime);
7411 }
7412 let left_bigint = self.bigint_text(left).map(str::to_owned);
7413 let right_bigint = self.bigint_text(right).map(str::to_owned);
7414 match (left_bigint, right_bigint) {
7415 (Some(left), Some(right)) => {
7416 let sum = bigint_i128(&left)?
7417 .checked_add(bigint_i128(&right)?)
7418 .ok_or(EvalFailure::Throw(ThrowOrigin::RangeError {
7419 operation: "bigint add overflow",
7420 }))?;
7421 return self
7422 .allocate(HeapEntry::BigInt(sum.to_string()))
7423 .map_err(EvalFailure::Runtime);
7424 }
7425 (Some(_), None) | (None, Some(_)) => {
7426 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7427 operation: "add bigint and number",
7428 }));
7429 }
7430 (None, None) => {}
7431 }
7432 let left = numeric_f64(self.to_number(left)?).unwrap();
7433 let right = numeric_f64(self.to_number(right)?).unwrap();
7434 Ok(number_value(left + right))
7435 }
7436
7437 fn numeric_binary(
7438 &mut self,
7439 op: BinaryOp,
7440 left: Value,
7441 right: Value,
7442 ) -> Result<Value, EvalFailure> {
7443 let left_bigint = self.bigint_text(left).map(str::to_owned);
7444 let right_bigint = self.bigint_text(right).map(str::to_owned);
7445 if left_bigint.is_some() || right_bigint.is_some() {
7446 let (Some(left), Some(right)) = (left_bigint, right_bigint) else {
7447 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7448 operation: "mix bigint and number",
7449 }));
7450 };
7451 let result = bigint_binary(op, &left, &right)?;
7452 return self
7453 .allocate(HeapEntry::BigInt(result))
7454 .map_err(EvalFailure::Runtime);
7455 }
7456 let left = numeric_f64(self.to_number(left)?).unwrap();
7457 let right = numeric_f64(self.to_number(right)?).unwrap();
7458 let value = match op {
7459 BinaryOp::Subtract => number_value(left - right),
7460 BinaryOp::Multiply => number_value(left * right),
7461 BinaryOp::Divide => Value::number(left / right),
7462 BinaryOp::Remainder => Value::number(left % right),
7463 BinaryOp::Exponent => Value::number(left.powf(right)),
7464 BinaryOp::BitAnd => Value::int32((to_int32(left) & to_int32(right)) as u32),
7465 BinaryOp::BitOr => Value::int32((to_int32(left) | to_int32(right)) as u32),
7466 BinaryOp::BitXor => Value::int32((to_int32(left) ^ to_int32(right)) as u32),
7467 BinaryOp::ShiftLeft => {
7468 Value::int32(to_int32(left).wrapping_shl(to_uint32(right) & 31) as u32)
7469 }
7470 BinaryOp::ShiftRight => {
7471 Value::int32((to_int32(left) >> (to_uint32(right) & 31)) as u32)
7472 }
7473 BinaryOp::UnsignedShiftRight => {
7474 number_value((to_uint32(left) >> (to_uint32(right) & 31)) as f64)
7475 }
7476 _ => unreachable!("numeric binary operator partition"),
7477 };
7478 Ok(value)
7479 }
7480
7481 fn coercion_is_primitive(&self, value: Value) -> Result<bool, EvalFailure> {
7482 let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
7483 return Ok(true);
7484 };
7485 Ok(matches!(
7486 self.heap[index],
7487 HeapEntry::String(_)
7488 | HeapEntry::BigInt(_)
7489 | HeapEntry::Symbol { .. }
7490 | HeapEntry::PrivateName { .. }
7491 ))
7492 }
7493
7494 fn to_primitive_default(&mut self, value: Value) -> Result<Value, EvalFailure> {
7495 let prefer_string = self
7496 .runtime_slot(value)
7497 .map_err(EvalFailure::Runtime)?
7498 .is_some_and(|index| matches!(self.heap[index], HeapEntry::Date { .. }));
7499 self.to_primitive_observable(value, prefer_string)
7500 }
7501
7502 pub(crate) fn to_primitive_observable(
7503 &mut self,
7504 value: Value,
7505 prefer_string: bool,
7506 ) -> Result<Value, EvalFailure> {
7507 if self.coercion_is_primitive(value)? {
7508 return Ok(value);
7509 }
7510 let methods = if prefer_string {
7511 ["toString", "valueOf"]
7512 } else {
7513 ["valueOf", "toString"]
7514 };
7515 for name in methods {
7516 let method = self.get_named_property(value, name)?;
7517 if !self.is_callable(method)? {
7518 continue;
7519 }
7520 let primitive = self.call_value(method, value, &[])?;
7521 if self.coercion_is_primitive(primitive)? {
7522 return Ok(primitive);
7523 }
7524 }
7525 Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7526 operation: "cannot convert object to primitive",
7527 }))
7528 }
7529
7530 pub(crate) fn to_string_observable(&mut self, value: Value) -> Result<EcmaString, EvalFailure> {
7531 let primitive = self.to_primitive_observable(value, true)?;
7532 self.to_string(primitive)
7533 }
7534
7535 pub(crate) fn to_number_observable(&mut self, value: Value) -> Result<Value, EvalFailure> {
7536 let primitive = self.to_primitive_observable(value, false)?;
7537 self.to_number(primitive)
7538 }
7539
7540 fn to_number(&self, value: Value) -> Result<Value, EvalFailure> {
7541 match value.decode() {
7542 Some(Decoded::Number(_)) | Some(Decoded::Int32(_)) => self.to_primitive(value),
7543 Some(Decoded::Undefined) => Ok(Value::number(f64::NAN)),
7544 Some(Decoded::Null) => Ok(Value::int32(0)),
7545 Some(Decoded::Boolean(value)) => Ok(Value::int32(u32::from(value))),
7546 Some(Decoded::Hole) | Some(Decoded::Uninitialized) => Ok(Value::number(f64::NAN)),
7547 Some(Decoded::HeapRef(_)) => {
7548 match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7549 Some(index) => match &self.heap[index] {
7550 HeapEntry::String(text) => Ok(number_value(parse_number(text))),
7551 HeapEntry::BigInt(_) => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7552 operation: "convert bigint to number",
7553 })),
7554 HeapEntry::Array { elements, .. } if elements.is_empty() => {
7555 Ok(Value::int32(0))
7556 }
7557 HeapEntry::Array { elements, .. } if elements.len() == 1 => {
7558 self.to_number(elements[0])
7559 }
7560 HeapEntry::Symbol { .. } | HeapEntry::PrivateName { .. } => {
7561 Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7562 operation: "convert symbol to number",
7563 }))
7564 }
7565 HeapEntry::Object { .. }
7566 | HeapEntry::Generator { .. }
7567 | HeapEntry::Script { .. }
7568 | HeapEntry::Array { .. }
7569 | HeapEntry::Function { .. }
7570 | HeapEntry::ModuleNamespace { .. }
7571 | HeapEntry::ExternalModuleNamespace { .. }
7572 | HeapEntry::HashState { .. }
7573 | HeapEntry::NativeFunction { .. }
7574 | HeapEntry::RegExp { .. }
7575 | HeapEntry::Date { .. }
7576 | HeapEntry::BuiltinIterator { .. }
7577 | HeapEntry::Collection { .. }
7578 | HeapEntry::Promise { .. }
7579 | HeapEntry::PromiseResolver { .. }
7580 | HeapEntry::PromiseFinally { .. }
7581 | HeapEntry::PromiseAll { .. }
7582 | HeapEntry::AsyncActivation { .. }
7583 | HeapEntry::PromiseAllElement { .. }
7584 | HeapEntry::ProcessEnv { .. }
7585 | HeapEntry::Iterator { .. }
7586 | HeapEntry::Timeout { .. } => Ok(Value::number(f64::NAN)),
7587 },
7588 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7589 operation: "coerce host object to number",
7590 })),
7591 }
7592 }
7593 None => Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
7594 value,
7595 })),
7596 }
7597 }
7598
7599 fn truthy(&self, value: Value) -> bool {
7600 match value.decode() {
7601 Some(Decoded::Number(number)) => number != 0.0 && !number.is_nan(),
7602 Some(Decoded::Int32(value)) => value != 0,
7603 Some(Decoded::Undefined | Decoded::Null | Decoded::Hole | Decoded::Uninitialized)
7604 | None => false,
7605 Some(Decoded::Boolean(value)) => value,
7606 Some(Decoded::HeapRef(_)) => match self.runtime_slot(value) {
7607 Ok(Some(index)) => match &self.heap[index] {
7608 HeapEntry::String(text) => !text.is_empty(),
7609 HeapEntry::BigInt(text) => text != "0",
7610 HeapEntry::Object { .. }
7611 | HeapEntry::Generator { .. }
7612 | HeapEntry::Script { .. }
7613 | HeapEntry::Array { .. }
7614 | HeapEntry::Function { .. }
7615 | HeapEntry::ModuleNamespace { .. }
7616 | HeapEntry::ExternalModuleNamespace { .. }
7617 | HeapEntry::HashState { .. }
7618 | HeapEntry::NativeFunction { .. }
7619 | HeapEntry::Symbol { .. }
7620 | HeapEntry::PrivateName { .. }
7621 | HeapEntry::RegExp { .. }
7622 | HeapEntry::Date { .. }
7623 | HeapEntry::BuiltinIterator { .. }
7624 | HeapEntry::Collection { .. }
7625 | HeapEntry::Promise { .. }
7626 | HeapEntry::PromiseResolver { .. }
7627 | HeapEntry::PromiseFinally { .. }
7628 | HeapEntry::PromiseAll { .. }
7629 | HeapEntry::AsyncActivation { .. }
7630 | HeapEntry::PromiseAllElement { .. }
7631 | HeapEntry::ProcessEnv { .. }
7632 | HeapEntry::Iterator { .. }
7633 | HeapEntry::Timeout { .. } => true,
7634 },
7635 Ok(None) => true,
7636 Err(_) => false,
7637 },
7638 }
7639 }
7640
7641 fn type_of(&self, value: Value) -> &'static str {
7642 match value.decode() {
7643 Some(Decoded::Undefined | Decoded::Hole | Decoded::Uninitialized) | None => "undefined",
7644 Some(Decoded::Number(_) | Decoded::Int32(_)) => "number",
7645 Some(Decoded::Null) => "object",
7646 Some(Decoded::Boolean(_)) => "boolean",
7647 Some(Decoded::HeapRef(_)) => match self.runtime_slot(value) {
7648 Ok(Some(index)) => match &self.heap[index] {
7649 HeapEntry::String(_) => "string",
7650 HeapEntry::BigInt(_) => "bigint",
7651 HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. } => "function",
7652 HeapEntry::Symbol { .. } => "symbol",
7653 HeapEntry::PrivateName { .. } => "object",
7654 HeapEntry::Object { .. }
7655 | HeapEntry::Generator { .. }
7656 | HeapEntry::Script { .. }
7657 | HeapEntry::Array { .. }
7658 | HeapEntry::ModuleNamespace { .. }
7659 | HeapEntry::ExternalModuleNamespace { .. }
7660 | HeapEntry::HashState { .. }
7661 | HeapEntry::RegExp { .. }
7662 | HeapEntry::Date { .. }
7663 | HeapEntry::BuiltinIterator { .. }
7664 | HeapEntry::Collection { .. }
7665 | HeapEntry::Promise { .. }
7666 | HeapEntry::PromiseResolver { .. }
7667 | HeapEntry::PromiseFinally { .. }
7668 | HeapEntry::PromiseAll { .. }
7669 | HeapEntry::AsyncActivation { .. }
7670 | HeapEntry::PromiseAllElement { .. }
7671 | HeapEntry::ProcessEnv { .. }
7672 | HeapEntry::Iterator { .. }
7673 | HeapEntry::Timeout { .. } => "object",
7674 },
7675 _ => "object",
7676 },
7677 }
7678 }
7679
7680 fn strict_equal(&self, left: Value, right: Value) -> bool {
7681 match (left.decode(), right.decode()) {
7682 (Some(Decoded::Number(a)), Some(Decoded::Number(b))) => a == b,
7683 (Some(Decoded::Number(a)), Some(Decoded::Int32(b)))
7684 | (Some(Decoded::Int32(b)), Some(Decoded::Number(a))) => a == f64::from(b as i32),
7685 (Some(Decoded::Int32(a)), Some(Decoded::Int32(b))) => a == b,
7686 (Some(Decoded::HeapRef(_)), Some(Decoded::HeapRef(_))) => {
7687 match (self.runtime_slot(left), self.runtime_slot(right)) {
7688 (Ok(Some(a)), Ok(Some(b))) => match (&self.heap[a], &self.heap[b]) {
7689 (HeapEntry::String(a), HeapEntry::String(b)) => a == b,
7690 (HeapEntry::BigInt(a), HeapEntry::BigInt(b)) => a == b,
7691 _ => left == right,
7692 },
7693 _ => left == right,
7694 }
7695 }
7696 _ => left == right,
7697 }
7698 }
7699
7700 fn abstract_equal(&self, left: Value, right: Value) -> Result<bool, EvalFailure> {
7701 if self.strict_equal(left, right) {
7702 return Ok(true);
7703 }
7704 if matches!(
7705 (left.decode(), right.decode()),
7706 (Some(Decoded::Null), Some(Decoded::Undefined))
7707 | (Some(Decoded::Undefined), Some(Decoded::Null))
7708 ) {
7709 return Ok(true);
7710 }
7711 let left_number = self.to_number(left);
7712 let right_number = self.to_number(right);
7713 match (left_number, right_number) {
7714 (Ok(left), Ok(right)) => Ok(numeric_f64(left).unwrap() == numeric_f64(right).unwrap()),
7715 _ => Ok(false),
7716 }
7717 }
7718
7719 fn relational_compare(
7720 &self,
7721 left: Value,
7722 right: Value,
7723 ) -> Result<Option<Ordering>, EvalFailure> {
7724 if let (Some(left), Some(right)) = (self.string_text(left), self.string_text(right)) {
7725 return Ok(Some(left.cmp(right)));
7726 }
7727 if let (Some(left), Some(right)) = (self.bigint_text(left), self.bigint_text(right)) {
7728 return Ok(Some(bigint_i128(left)?.cmp(&bigint_i128(right)?)));
7729 }
7730 let left = numeric_f64(self.to_number(left)?).unwrap();
7731 let right = numeric_f64(self.to_number(right)?).unwrap();
7732 Ok(left.partial_cmp(&right))
7733 }
7734
7735 fn instance_of(&mut self, value: Value, constructor: Value) -> Result<bool, EvalFailure> {
7738 let constructor = self
7739 .bound_target(constructor)
7740 .map_err(EvalFailure::Runtime)?;
7741 match self
7742 .runtime_slot(constructor)
7743 .map_err(EvalFailure::Runtime)?
7744 {
7745 Some(index) => {
7746 if !matches!(
7747 self.heap[index],
7748 HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. }
7749 ) {
7750 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7751 operation: "instanceof",
7752 }));
7753 }
7754 let target = match self.own_get_ascii(index, "prototype") {
7755 Some(Found::Value(value)) if self.is_object(value) => value,
7756 _ => {
7757 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7758 operation: "instanceof prototype is not an object",
7759 }));
7760 }
7761 };
7762 let target_slot = self.runtime_slot(target).map_err(EvalFailure::Runtime)?;
7763 let mut node = match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7764 Some(node) => node,
7765 None => return Ok(false),
7766 };
7767 let mut guard = 0;
7768 loop {
7769 if Some(node) == target_slot {
7770 return Ok(true);
7771 }
7772 match self.prototype_index(node)? {
7773 Some(next) => {
7774 node = next;
7775 guard += 1;
7776 if guard > self.heap.len() + 1 {
7777 return Ok(false);
7778 }
7779 }
7780 None => return Ok(false),
7781 }
7782 }
7783 }
7784 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7785 operation: "instanceof",
7786 })),
7787 }
7788 }
7789
7790 fn value_to_string(&self, value: Value, depth: usize) -> Result<EcmaString, EvalFailure> {
7791 if depth >= 32 {
7792 return Ok(EcmaString::default());
7793 }
7794 let ascii = |text: String| EcmaString::from_utf8(&text);
7795 match value.decode() {
7796 Some(Decoded::Number(number)) => Ok(ascii(Self::ordinary_number_to_string(number))),
7797 Some(Decoded::Int32(raw)) => Ok(ascii((raw as i32).to_string())),
7798 Some(Decoded::Undefined | Decoded::Uninitialized) => {
7799 Ok(EcmaString::from_utf8("undefined"))
7800 }
7801 Some(Decoded::Null) => Ok(EcmaString::from_utf8("null")),
7802 Some(Decoded::Boolean(value)) => {
7803 Ok(EcmaString::from_utf8(if value { "true" } else { "false" }))
7804 }
7805 Some(Decoded::Hole) => Ok(EcmaString::default()),
7806 Some(Decoded::HeapRef(_)) => {
7807 match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7808 Some(index) => match &self.heap[index] {
7809 HeapEntry::String(text) => Ok(text.clone()),
7810 HeapEntry::BigInt(text) => Ok(EcmaString::from_utf8(text)),
7811 HeapEntry::Object { .. }
7812 | HeapEntry::Generator { .. }
7813 | HeapEntry::Script { .. }
7814 | HeapEntry::Date { .. }
7815 | HeapEntry::BuiltinIterator { .. }
7816 | HeapEntry::Collection { .. }
7817 | HeapEntry::Promise { .. }
7818 | HeapEntry::PromiseResolver { .. }
7819 | HeapEntry::PromiseFinally { .. }
7820 | HeapEntry::PromiseAll { .. }
7821 | HeapEntry::AsyncActivation { .. }
7822 | HeapEntry::PromiseAllElement { .. }
7823 | HeapEntry::ModuleNamespace { .. }
7824 | HeapEntry::ExternalModuleNamespace { .. }
7825 | HeapEntry::ProcessEnv { .. }
7826 | HeapEntry::Iterator { .. }
7827 | HeapEntry::Timeout { .. }
7828 | HeapEntry::HashState { .. } => {
7829 Ok(EcmaString::from_utf8("[object Object]"))
7830 }
7831 HeapEntry::RegExp { pattern, flags, .. } => {
7832 let mut builder = EcmaStringBuilder::with_capacity(
7833 pattern
7834 .len_units()
7835 .saturating_add(flags.len_units())
7836 .saturating_add(2),
7837 );
7838 builder.push_unit(u16::from(b'/'));
7839 for &unit in pattern.as_units() {
7840 builder.push_unit(unit);
7841 }
7842 builder.push_unit(u16::from(b'/'));
7843 for &unit in flags.as_units() {
7844 builder.push_unit(unit);
7845 }
7846 Ok(builder.finish())
7847 }
7848 HeapEntry::Symbol { .. } => {
7849 Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7850 operation: "convert symbol to string",
7851 }))
7852 }
7853 HeapEntry::PrivateName { .. } => {
7854 Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7855 operation: "convert private name to string",
7856 }))
7857 }
7858 HeapEntry::Function {
7859 module, function, ..
7860 } => {
7861 let flags = self.module_code(*module).functions()
7862 [function.get() as usize]
7863 .flags();
7864 Ok(EcmaString::from_utf8(
7865 match (flags.is_async, flags.is_generator) {
7866 (true, true) => "async function* () { [bytecode] }",
7867 (true, false) => "async function () { [bytecode] }",
7868 (false, true) => "function* () { [bytecode] }",
7869 (false, false) => "function () { [bytecode] }",
7870 },
7871 ))
7872 }
7873 HeapEntry::NativeFunction { .. } => {
7874 Ok(EcmaString::from_utf8("function () { [native code] }"))
7875 }
7876 HeapEntry::Array { elements, .. } => {
7877 let mut text = EcmaStringBuilder::new();
7878 for (index, element) in elements.iter().copied().enumerate() {
7879 if index != 0 {
7880 text.push_unit(u16::from(b','));
7881 }
7882 if element != Value::HOLE
7883 && element != Value::NULL
7884 && element != Value::UNDEFINED
7885 {
7886 for &unit in
7887 self.value_to_string(element, depth + 1)?.as_units()
7888 {
7889 text.push_unit(unit);
7890 }
7891 }
7892 }
7893 Ok(text.finish())
7894 }
7895 },
7896 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7897 operation: "coerce host object to string",
7898 })),
7899 }
7900 }
7901 None => Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
7902 value,
7903 })),
7904 }
7905 }
7906
7907 fn string_text(&self, value: Value) -> Option<&EcmaString> {
7908 let index = self.runtime_slot(value).ok()??;
7909 match &self.heap[index] {
7910 HeapEntry::String(text) => Some(text),
7911 _ => None,
7912 }
7913 }
7914
7915 fn bigint_text(&self, value: Value) -> Option<&str> {
7916 let index = self.runtime_slot(value).ok()??;
7917 match &self.heap[index] {
7918 HeapEntry::BigInt(text) => Some(text),
7919 _ => None,
7920 }
7921 }
7922
7923 fn is_object(&self, value: Value) -> bool {
7924 match self.runtime_slot(value) {
7925 Ok(Some(index)) => !matches!(
7926 self.heap[index],
7927 HeapEntry::String(_)
7928 | HeapEntry::BigInt(_)
7929 | HeapEntry::PromiseResolver { .. }
7930 | HeapEntry::PromiseFinally { .. }
7931 | HeapEntry::PromiseAll { .. }
7932 | HeapEntry::AsyncActivation { .. }
7933 | HeapEntry::PromiseAllElement { .. }
7934 ),
7935 Ok(None) => matches!(value.decode(), Some(Decoded::HeapRef(_))),
7936 Err(_) => false,
7937 }
7938 }
7939}
7940
7941fn ordered_property_keys(properties: &PropertyMap) -> Vec<PropertyKey> {
7942 let mut indices = Vec::new();
7943 let mut strings = Vec::new();
7944 let mut symbols = Vec::new();
7945 for (key, _) in properties.iter() {
7946 match key {
7947 PropertyKey::Named(name) => match array_index(name) {
7948 Some(index) => indices.push((index, key.clone())),
7949 None => strings.push(key.clone()),
7950 },
7951 PropertyKey::Symbol(_) => symbols.push(key.clone()),
7952 PropertyKey::Private(_) => {}
7953 }
7954 }
7955 indices.sort_unstable_by_key(|(index, _)| *index);
7956 indices
7957 .into_iter()
7958 .map(|(_, key)| key)
7959 .chain(strings)
7960 .chain(symbols)
7961 .collect()
7962}
7963
7964fn property_lookup(properties: &PropertyMap, key: &PropertyKey) -> Option<Found> {
7965 match properties.get(key) {
7966 Some(Property::Data { value, .. }) => Some(Found::Value(*value)),
7967 Some(Property::Accessor { getter, .. }) => Some(match getter {
7968 Some(getter) => Found::Getter(*getter),
7969 None => Found::NoGetter,
7970 }),
7971 None => None,
7972 }
7973}
7974
7975fn property_lookup_ascii(properties: &PropertyMap, name: &str) -> Option<Found> {
7976 match properties.get_ascii(name) {
7977 Some(Property::Data { value, .. }) => Some(Found::Value(*value)),
7978 Some(Property::Accessor { getter, .. }) => Some(match getter {
7979 Some(getter) => Found::Getter(*getter),
7980 None => Found::NoGetter,
7981 }),
7982 None => None,
7983 }
7984}
7985
7986fn innermost_handler(function: &Function, pc: usize) -> Option<bamts_bytecode::ExceptionHandler> {
7987 function
7988 .handlers()
7989 .iter()
7990 .copied()
7991 .filter(|handler| handler.start.get() as usize <= pc && pc < handler.end.get() as usize)
7992 .max_by(|left, right| {
7993 left.start
7994 .get()
7995 .cmp(&right.start.get())
7996 .then_with(|| right.end.get().cmp(&left.end.get()))
7997 })
7998}
7999
8000fn numeric_f64(value: Value) -> Option<f64> {
8001 match value.decode()? {
8002 Decoded::Number(number) => Some(number),
8003 Decoded::Int32(raw) => Some(f64::from(raw as i32)),
8004 _ => None,
8005 }
8006}
8007
8008fn number_value(number: f64) -> Value {
8009 if number.is_finite()
8010 && number.fract() == 0.0
8011 && number >= f64::from(i32::MIN)
8012 && number <= f64::from(i32::MAX)
8013 {
8014 Value::int32(number as i32 as u32)
8015 } else {
8016 Value::number(number)
8017 }
8018}
8019
8020fn parse_number(text: &EcmaString) -> f64 {
8021 let Ok(text) = text.to_utf8_strict() else {
8022 return f64::NAN;
8023 };
8024 parse_number_utf8(&text)
8025}
8026
8027fn parse_number_utf8(text: &str) -> f64 {
8028 let trimmed = text.trim();
8029 if trimmed.is_empty() {
8030 0.0
8031 } else {
8032 trimmed.parse::<f64>().unwrap_or(f64::NAN)
8033 }
8034}
8035
8036fn format_number(number: f64) -> String {
8037 if number.is_nan() {
8038 return "NaN".to_owned();
8039 }
8040 if number == f64::INFINITY {
8041 return "Infinity".to_owned();
8042 }
8043 if number == f64::NEG_INFINITY {
8044 return "-Infinity".to_owned();
8045 }
8046 if number == 0.0 {
8047 return "0".to_owned();
8048 }
8049
8050 let negative = number.is_sign_negative();
8051 let raw = number.abs().to_string();
8052 let (mantissa, explicit_exponent) = match raw.split_once(['e', 'E']) {
8053 Some((mantissa, exponent)) => (
8054 mantissa,
8055 exponent
8056 .parse::<i32>()
8057 .expect("Rust formats finite f64 exponents as i32"),
8058 ),
8059 None => (raw.as_str(), 0),
8060 };
8061 let decimal = mantissa.find('.').unwrap_or(mantissa.len());
8062 let untrimmed: String = mantissa.chars().filter(|ch| *ch != '.').collect();
8063 let first = untrimmed
8064 .find(|ch| ch != '0')
8065 .expect("a nonzero number has a nonzero decimal digit");
8066 let digits = untrimmed[first..].trim_end_matches('0');
8067 let exponent = explicit_exponent + decimal as i32 - first as i32 - 1;
8068
8069 let mut result = String::new();
8070 if negative {
8071 result.push('-');
8072 }
8073 if !(-6..21).contains(&exponent) {
8074 result.push(digits.as_bytes()[0] as char);
8075 if digits.len() > 1 {
8076 result.push('.');
8077 result.push_str(&digits[1..]);
8078 }
8079 result.push('e');
8080 if exponent >= 0 {
8081 result.push('+');
8082 }
8083 result.push_str(&exponent.to_string());
8084 } else if exponent >= 0 {
8085 let integer_digits = exponent as usize + 1;
8086 if digits.len() <= integer_digits {
8087 result.push_str(digits);
8088 result.extend(std::iter::repeat_n('0', integer_digits - digits.len()));
8089 } else {
8090 result.push_str(&digits[..integer_digits]);
8091 result.push('.');
8092 result.push_str(&digits[integer_digits..]);
8093 }
8094 } else {
8095 result.push_str("0.");
8096 result.extend(std::iter::repeat_n('0', (-exponent - 1) as usize));
8097 result.push_str(digits);
8098 }
8099 result
8100}
8101
8102fn to_uint32(number: f64) -> u32 {
8103 if !number.is_finite() || number == 0.0 {
8104 0
8105 } else {
8106 number.trunc().rem_euclid(4_294_967_296.0) as u32
8107 }
8108}
8109
8110fn to_int32(number: f64) -> i32 {
8111 to_uint32(number) as i32
8112}
8113
8114fn array_index_ascii(key: &str) -> Option<u32> {
8115 if !key.is_ascii() || key.is_empty() || (key.len() > 1 && key.as_bytes()[0] == b'0') {
8116 return None;
8117 }
8118 let mut index = 0_u32;
8119 for byte in key.bytes() {
8120 if !byte.is_ascii_digit() {
8121 return None;
8122 }
8123 index = index.checked_mul(10)?.checked_add(u32::from(byte - b'0'))?;
8124 }
8125 (index != u32::MAX).then_some(index)
8126}
8127
8128fn array_index(key: &EcmaString) -> Option<u32> {
8129 let units = key.as_units();
8130 if units.is_empty() || (units.len() > 1 && units[0] == u16::from(b'0')) {
8131 return None;
8132 }
8133 let mut index = 0_u32;
8134 for &unit in units {
8135 if !(u16::from(b'0')..=u16::from(b'9')).contains(&unit) {
8136 return None;
8137 }
8138 index = index
8139 .checked_mul(10)?
8140 .checked_add(u32::from(unit - u16::from(b'0')))?;
8141 }
8142 (index != u32::MAX).then_some(index)
8143}
8144
8145fn exact_array_length(value: Value) -> Option<usize> {
8146 let number = numeric_f64(value)?;
8147 if number.is_finite() && number >= 0.0 && number.fract() == 0.0 && number <= u32::MAX as f64 {
8148 Some(number as usize)
8149 } else {
8150 None
8151 }
8152}
8153
8154pub(crate) fn apply_array_length(
8155 elements: &mut Vec<Value>,
8156 properties: &mut PropertyMap,
8157 length: usize,
8158 operation: &'static str,
8159) -> Result<(), EvalFailure> {
8160 if length >= elements.len() {
8161 elements.resize(length, Value::HOLE);
8162 return Ok(());
8163 }
8164 let blocked = properties
8165 .iter()
8166 .filter_map(|(key, property)| {
8167 (!property.configurable())
8168 .then(|| key.as_string().and_then(array_index))
8169 .flatten()
8170 })
8171 .map(|offset| offset as usize)
8172 .filter(|offset| *offset >= length)
8173 .max();
8174 let effective_length = blocked.map_or(length, |offset| offset + 1);
8175 properties.0.retain(|(key, _)| {
8176 key.as_string()
8177 .and_then(array_index)
8178 .is_none_or(|offset| (offset as usize) < effective_length)
8179 });
8180 elements.resize(effective_length, Value::HOLE);
8181 if blocked.is_some() {
8182 return Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }));
8183 }
8184 Ok(())
8185}
8186
8187pub(crate) fn array_set_length(
8188 elements: &mut Vec<Value>,
8189 properties: &mut PropertyMap,
8190 length_writable: bool,
8191 value: Value,
8192 operation: &'static str,
8193) -> Result<(), EvalFailure> {
8194 let length = exact_array_length(value)
8195 .ok_or(EvalFailure::Throw(ThrowOrigin::RangeError { operation }))?;
8196 if !length_writable {
8197 return Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }));
8198 }
8199 apply_array_length(elements, properties, length, operation)
8200}
8201
8202fn bigint_i128(text: &str) -> Result<i128, EvalFailure> {
8203 text.parse::<i128>().map_err(|_| {
8204 EvalFailure::Throw(ThrowOrigin::RangeError {
8205 operation: "bigint magnitude exceeds runtime width",
8206 })
8207 })
8208}
8209
8210fn bigint_binary(op: BinaryOp, left: &str, right: &str) -> Result<String, EvalFailure> {
8211 let left = bigint_i128(left)?;
8212 let right = bigint_i128(right)?;
8213 let overflow =
8214 |operation: &'static str| EvalFailure::Throw(ThrowOrigin::RangeError { operation });
8215 let result = match op {
8216 BinaryOp::Subtract => left
8217 .checked_sub(right)
8218 .ok_or_else(|| overflow("bigint subtract overflow"))?,
8219 BinaryOp::Multiply => left
8220 .checked_mul(right)
8221 .ok_or_else(|| overflow("bigint multiply overflow"))?,
8222 BinaryOp::Divide => {
8223 if right == 0 {
8224 return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8225 operation: "bigint division by zero",
8226 }));
8227 }
8228 left.checked_div(right)
8229 .ok_or_else(|| overflow("bigint divide overflow"))?
8230 }
8231 BinaryOp::Remainder => {
8232 if right == 0 {
8233 return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8234 operation: "bigint remainder by zero",
8235 }));
8236 }
8237 left.checked_rem(right)
8238 .ok_or_else(|| overflow("bigint remainder overflow"))?
8239 }
8240 BinaryOp::Exponent => {
8241 if right < 0 {
8242 return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8243 operation: "bigint negative exponent",
8244 }));
8245 }
8246 let exponent =
8247 u32::try_from(right).map_err(|_| overflow("bigint exponent overflow"))?;
8248 left.checked_pow(exponent)
8249 .ok_or_else(|| overflow("bigint exponent overflow"))?
8250 }
8251 BinaryOp::BitAnd => left & right,
8252 BinaryOp::BitOr => left | right,
8253 BinaryOp::BitXor => left ^ right,
8254 BinaryOp::ShiftLeft | BinaryOp::ShiftRight => {
8255 let left_shift = (op == BinaryOp::ShiftLeft) == (right >= 0);
8256 let amount =
8257 u32::try_from(right.unsigned_abs()).map_err(|_| overflow("bigint shift width"))?;
8258 let shifted = if left_shift {
8259 left.checked_shl(amount)
8260 } else {
8261 left.checked_shr(amount)
8262 };
8263 shifted.ok_or_else(|| overflow("bigint shift overflow"))?
8264 }
8265 BinaryOp::UnsignedShiftRight => {
8266 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
8267 operation: "unsigned shift on bigint",
8268 }));
8269 }
8270 _ => unreachable!("bigint arithmetic partition"),
8271 };
8272 Ok(result.to_string())
8273}
8274
8275pub(crate) fn unary_from_selector(op: u32) -> Option<UnaryOp> {
8276 match op {
8277 0 => Some(UnaryOp::Void),
8278 1 => Some(UnaryOp::TypeOf),
8279 2 => Some(UnaryOp::Plus),
8280 3 => Some(UnaryOp::Negate),
8281 4 => Some(UnaryOp::BitwiseNot),
8282 5 => Some(UnaryOp::LogicalNot),
8283 _ => None,
8284 }
8285}
8286
8287pub(crate) fn binary_from_selector(op: u32) -> Option<BinaryOp> {
8288 match op {
8289 0 => Some(BinaryOp::Add),
8290 1 => Some(BinaryOp::Subtract),
8291 2 => Some(BinaryOp::Multiply),
8292 3 => Some(BinaryOp::Divide),
8293 4 => Some(BinaryOp::Remainder),
8294 5 => Some(BinaryOp::Exponent),
8295 6 => Some(BinaryOp::BitAnd),
8296 7 => Some(BinaryOp::BitOr),
8297 8 => Some(BinaryOp::BitXor),
8298 9 => Some(BinaryOp::ShiftLeft),
8299 10 => Some(BinaryOp::ShiftRight),
8300 11 => Some(BinaryOp::UnsignedShiftRight),
8301 12 => Some(BinaryOp::Equal),
8302 13 => Some(BinaryOp::NotEqual),
8303 14 => Some(BinaryOp::StrictEqual),
8304 15 => Some(BinaryOp::StrictNotEqual),
8305 16 => Some(BinaryOp::LessThan),
8306 17 => Some(BinaryOp::LessThanOrEqual),
8307 18 => Some(BinaryOp::GreaterThan),
8308 19 => Some(BinaryOp::GreaterThanOrEqual),
8309 20 => Some(BinaryOp::InstanceOf),
8310 21 => Some(BinaryOp::In),
8311 _ => None,
8312 }
8313}
8314
8315pub(crate) fn iterator_kind_from_selector(kind: u32) -> Option<IteratorKind> {
8316 match kind {
8317 0 => Some(IteratorKind::Sync),
8318 1 => Some(IteratorKind::Async),
8319 2 => Some(IteratorKind::Keys),
8320 _ => None,
8321 }
8322}
8323
8324pub(crate) fn accessor_from_selector(kind: u32) -> Option<AccessorKind> {
8325 match kind {
8326 0 => Some(AccessorKind::Getter),
8327 1 => Some(AccessorKind::Setter),
8328 _ => None,
8329 }
8330}
8331
8332#[cfg(test)]
8333mod tests {
8334 use std::sync::Arc;
8335
8336 use super::*;
8337 use crate::intrinsics::BuiltinOutcome;
8338 use bamts_bytecode::{
8339 Binding, Edge, EdgeKind, ExceptionHandler, Export, ExportSource, FunctionFlags, NumberBits,
8340 ProgramModule, Register,
8341 };
8342
8343 fn reg(raw: u32) -> Register {
8344 Register::new(raw)
8345 }
8346 fn pc(raw: u32) -> Pc {
8347 Pc::new(raw)
8348 }
8349 fn cid(raw: u32) -> ConstantId {
8350 ConstantId::new(raw)
8351 }
8352
8353 fn function(
8355 parameters: u32,
8356 registers: u32,
8357 code: Vec<Instruction>,
8358 handlers: Vec<ExceptionHandler>,
8359 ) -> Function {
8360 Function::new(
8361 None,
8362 0,
8363 parameters,
8364 registers,
8365 FunctionFlags::default(),
8366 code,
8367 handlers,
8368 )
8369 }
8370
8371 fn generator_function(
8372 parameters: u32,
8373 registers: u32,
8374 code: Vec<Instruction>,
8375 handlers: Vec<ExceptionHandler>,
8376 ) -> Function {
8377 Function::new(
8378 None,
8379 0,
8380 parameters,
8381 registers,
8382 FunctionFlags {
8383 is_async: false,
8384 is_generator: true,
8385 },
8386 code,
8387 handlers,
8388 )
8389 }
8390
8391 fn async_function(
8392 parameters: u32,
8393 registers: u32,
8394 code: Vec<Instruction>,
8395 handlers: Vec<ExceptionHandler>,
8396 ) -> Function {
8397 Function::new(
8398 None,
8399 0,
8400 parameters,
8401 registers,
8402 FunctionFlags {
8403 is_async: true,
8404 is_generator: false,
8405 },
8406 code,
8407 handlers,
8408 )
8409 }
8410
8411 fn closure_function(
8413 captures: u32,
8414 parameters: u32,
8415 registers: u32,
8416 code: Vec<Instruction>,
8417 ) -> Function {
8418 Function::new(
8419 None,
8420 captures,
8421 parameters,
8422 registers,
8423 FunctionFlags::default(),
8424 code,
8425 Vec::new(),
8426 )
8427 }
8428
8429 fn verified(mut constants: Vec<Constant>, functions: Vec<Function>) -> Program<Verified> {
8430 let name = ConstantId::new(constants.len() as u32);
8431 constants.push(Constant::String(EcmaString::from_utf8("<test>")));
8432 let code = Module::new(constants, functions, FunctionId::new(0))
8433 .verify()
8434 .expect("valid test bytecode");
8435 Program::link(
8436 vec![ProgramModule {
8437 name,
8438 code,
8439 edges: Vec::new(),
8440 bindings: Vec::new(),
8441 exports: Vec::new(),
8442 }],
8443 ModuleId::new(0),
8444 )
8445 .expect("valid one-module test program")
8446 }
8447 fn program_module(
8448 name: &str,
8449 mut constants: Vec<Constant>,
8450 functions: Vec<Function>,
8451 edges: Vec<Edge>,
8452 bindings: Vec<Binding>,
8453 exports: Vec<Export>,
8454 ) -> ProgramModule<Verified> {
8455 constants.insert(0, Constant::String(EcmaString::from_utf8(name)));
8456 let code = Module::new(constants, functions, FunctionId::new(0))
8457 .verify()
8458 .expect("valid test bytecode");
8459 ProgramModule {
8460 name: ConstantId::new(0),
8461 code,
8462 edges,
8463 bindings,
8464 exports,
8465 }
8466 }
8467
8468 fn linked(modules: Vec<ProgramModule<Verified>>, entry: u32) -> Program<Verified> {
8469 Program::link(modules, ModuleId::new(entry)).expect("valid linked test program")
8470 }
8471
8472 fn namespace_descriptor_entry() -> Function {
8473 function(
8474 0,
8475 7,
8476 vec![
8477 Instruction::LoadGlobal {
8478 dst: reg(0),
8479 name: cid(1),
8480 },
8481 Instruction::LoadGlobal {
8482 dst: reg(1),
8483 name: cid(3),
8484 },
8485 Instruction::LoadConst {
8486 dst: reg(2),
8487 constant: cid(4),
8488 },
8489 Instruction::GetProperty {
8490 dst: reg(3),
8491 object: reg(1),
8492 key: reg(2),
8493 },
8494 Instruction::CreateArray { dst: reg(4) },
8495 Instruction::ArrayPush {
8496 array: reg(4),
8497 value: reg(0),
8498 },
8499 Instruction::LoadConst {
8500 dst: reg(5),
8501 constant: cid(5),
8502 },
8503 Instruction::ArrayPush {
8504 array: reg(4),
8505 value: reg(5),
8506 },
8507 Instruction::Call {
8508 dst: reg(6),
8509 callee: reg(3),
8510 this_value: reg(4),
8511 arguments: reg(4),
8512 },
8513 Instruction::Return { value: reg(6) },
8514 ],
8515 Vec::new(),
8516 )
8517 }
8518
8519 #[derive(Default)]
8520 struct TestHost;
8521 impl Host for TestHost {}
8522
8523 #[test]
8524 fn async_await_setup_failure_releases_suspended_registers() {
8525 let program = verified(
8526 vec![Constant::Undefined],
8527 vec![
8528 function(0, 1, vec![Instruction::Halt], Vec::new()),
8529 async_function(
8530 0,
8531 2,
8532 vec![
8533 Instruction::LoadConst {
8534 dst: reg(0),
8535 constant: cid(0),
8536 },
8537 Instruction::Suspend {
8538 dst: reg(1),
8539 src: reg(0),
8540 resume: pc(2),
8541 },
8542 Instruction::Return { value: reg(1) },
8543 ],
8544 Vec::new(),
8545 ),
8546 ],
8547 );
8548 let mut host = TestHost;
8549 let limits = Limits {
8550 max_microtasks: 0,
8551 ..Limits::default()
8552 };
8553 let mut machine = Machine::new(&program, &mut host, limits);
8554 machine.frames.clear();
8555 machine.live_registers = 0;
8556 let callable = generator_callable(&mut machine, 1);
8557
8558 assert!(matches!(
8559 machine.call_value(callable, Value::UNDEFINED, &[]),
8560 Err(EvalFailure::Runtime(
8561 RuntimeErrorKind::MicrotaskQueueLimitExceeded { limit: 0 }
8562 ))
8563 ));
8564 assert_eq!(machine.live_registers, 0);
8565 }
8566
8567 fn run_ok(program: &Program<Verified>) -> Execution {
8568 let mut host = TestHost;
8569 Machine::new(program, &mut host, Limits::default())
8570 .run()
8571 .unwrap()
8572 }
8573
8574 fn generator_callable<H: Host>(machine: &mut Machine<'_, H>, function: u32) -> Value {
8575 machine
8576 .allocate(HeapEntry::Function {
8577 module: ModuleId::new(0),
8578 function: FunctionId::new(function),
8579 captures: Vec::new(),
8580 properties: PropertyMap::default(),
8581 prototype: Some(machine.intrinsics.function_prototype),
8582 extensible: true,
8583 })
8584 .unwrap()
8585 }
8586
8587 fn generator_next<H: Host>(
8588 machine: &mut Machine<'_, H>,
8589 generator: Value,
8590 resume_value: Value,
8591 ) -> Result<(Value, bool), EvalFailure> {
8592 let next = machine.get_named_property(generator, "next")?;
8593 let result = machine.call_value(next, generator, &[resume_value])?;
8594 let done = machine.get_named_property(result, "done")?;
8595 let value = machine.get_named_property(result, "value")?;
8596 Ok((value, machine.truthy(done)))
8597 }
8598
8599 #[test]
8600 fn sync_generator_is_lazy_resumes_registers_and_stays_completed() {
8601 let program = verified(
8602 vec![Constant::Int32(10)],
8603 vec![
8604 function(0, 1, vec![Instruction::Halt], Vec::new()),
8605 generator_function(
8606 0,
8607 3,
8608 vec![
8609 Instruction::LoadConst {
8610 dst: reg(0),
8611 constant: cid(0),
8612 },
8613 Instruction::Suspend {
8614 dst: reg(1),
8615 src: reg(0),
8616 resume: pc(2),
8617 },
8618 Instruction::Binary {
8619 dst: reg(2),
8620 op: BinaryOp::Add,
8621 left: reg(0),
8622 right: reg(1),
8623 },
8624 Instruction::Return { value: reg(2) },
8625 ],
8626 Vec::new(),
8627 ),
8628 ],
8629 );
8630 let mut host = TestHost;
8631 let mut machine = Machine::new(&program, &mut host, Limits::default());
8632 machine.frames.clear();
8633 machine.live_registers = 0;
8634 let callable = generator_callable(&mut machine, 1);
8635 let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8636 assert_eq!(machine.live_registers, 0, "calling must not start the body");
8637 machine
8638 .set_data_property(generator, "visible", Value::int32(1))
8639 .unwrap();
8640 assert_eq!(
8641 machine.get_named_property(generator, "visible").unwrap(),
8642 Value::int32(1),
8643 );
8644 assert_eq!(
8645 machine.own_property_keys(generator).unwrap(),
8646 vec![PropertyKey::Named(EcmaString::from_utf8("visible"))],
8647 );
8648 assert!(
8649 machine
8650 .inherits_from_prototype(
8651 generator,
8652 machine.intrinsics.builtins.generator_prototype(),
8653 )
8654 .unwrap()
8655 );
8656
8657 assert_eq!(
8658 generator_next(&mut machine, generator, Value::int32(99)).unwrap(),
8659 (Value::int32(10), false),
8660 );
8661 assert_eq!(machine.live_registers, 3);
8662 assert_eq!(
8663 generator_next(&mut machine, generator, Value::int32(5)).unwrap(),
8664 (Value::int32(15), true),
8665 );
8666 assert_eq!(machine.live_registers, 0);
8667 assert_eq!(
8668 generator_next(&mut machine, generator, Value::int32(8)).unwrap(),
8669 (Value::UNDEFINED, true),
8670 );
8671 }
8672
8673 #[test]
8674 fn sync_generator_reentrant_next_is_a_type_error() {
8675 let program = verified(
8676 Vec::new(),
8677 vec![
8678 function(0, 1, vec![Instruction::Halt], Vec::new()),
8679 generator_function(0, 1, vec![Instruction::Halt], Vec::new()),
8680 ],
8681 );
8682 let mut host = TestHost;
8683 let mut machine = Machine::new(&program, &mut host, Limits::default());
8684 machine.frames.clear();
8685 machine.live_registers = 0;
8686 let callable = generator_callable(&mut machine, 1);
8687 let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8688 let _ = machine.take_generator_state(generator).unwrap();
8689
8690 assert!(matches!(
8691 generator_next(&mut machine, generator, Value::UNDEFINED),
8692 Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
8693 ));
8694 }
8695
8696 #[test]
8697 fn sync_generator_uncaught_throw_preserves_origin_and_completes() {
8698 let program = verified(
8699 vec![Constant::Int32(7)],
8700 vec![
8701 function(0, 1, vec![Instruction::Halt], Vec::new()),
8702 generator_function(
8703 0,
8704 1,
8705 vec![
8706 Instruction::LoadConst {
8707 dst: reg(0),
8708 constant: cid(0),
8709 },
8710 Instruction::Throw { value: reg(0) },
8711 ],
8712 Vec::new(),
8713 ),
8714 ],
8715 );
8716 let mut host = TestHost;
8717 let mut machine = Machine::new(&program, &mut host, Limits::default());
8718 machine.frames.clear();
8719 machine.live_registers = 0;
8720 let callable = generator_callable(&mut machine, 1);
8721 let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8722
8723 assert!(matches!(
8724 generator_next(&mut machine, generator, Value::UNDEFINED),
8725 Err(EvalFailure::ThrowValueOrigin {
8726 value,
8727 origin: ThrowOrigin::Bytecode,
8728 }) if value == Value::int32(7)
8729 ));
8730 assert_eq!(
8731 generator_next(&mut machine, generator, Value::UNDEFINED).unwrap(),
8732 (Value::UNDEFINED, true),
8733 );
8734 assert_eq!(machine.live_registers, 0);
8735 }
8736
8737 #[test]
8738 fn outer_compiled_handler_catches_generator_throw_value() {
8739 let program = verified(
8740 vec![
8741 Constant::Int32(7),
8742 Constant::Undefined,
8743 Constant::String(EcmaString::from_utf8("next")),
8744 ],
8745 vec![
8746 function(
8747 0,
8748 8,
8749 vec![
8750 Instruction::CreateArray { dst: reg(0) },
8751 Instruction::CreateClosure {
8752 dst: reg(1),
8753 function: FunctionId::new(1),
8754 captures: reg(0),
8755 },
8756 Instruction::CreateArray { dst: reg(2) },
8757 Instruction::LoadConst {
8758 dst: reg(3),
8759 constant: cid(1),
8760 },
8761 Instruction::Call {
8762 dst: reg(4),
8763 callee: reg(1),
8764 this_value: reg(3),
8765 arguments: reg(2),
8766 },
8767 Instruction::LoadConst {
8768 dst: reg(5),
8769 constant: cid(2),
8770 },
8771 Instruction::GetProperty {
8772 dst: reg(6),
8773 object: reg(4),
8774 key: reg(5),
8775 },
8776 Instruction::Call {
8777 dst: reg(7),
8778 callee: reg(6),
8779 this_value: reg(4),
8780 arguments: reg(2),
8781 },
8782 Instruction::Return { value: reg(3) },
8783 Instruction::Return { value: reg(7) },
8784 ],
8785 vec![ExceptionHandler {
8786 start: pc(7),
8787 end: pc(8),
8788 handler: pc(9),
8789 catch_register: reg(7),
8790 }],
8791 ),
8792 generator_function(
8793 0,
8794 1,
8795 vec![
8796 Instruction::LoadConst {
8797 dst: reg(0),
8798 constant: cid(0),
8799 },
8800 Instruction::Throw { value: reg(0) },
8801 ],
8802 Vec::new(),
8803 ),
8804 ],
8805 );
8806
8807 assert_eq!(run_ok(&program).value, Value::int32(7));
8808 }
8809
8810 #[test]
8811 fn sync_generator_catches_body_throw_before_suspending() {
8812 let program = verified(
8813 vec![Constant::Int32(7)],
8814 vec![
8815 function(0, 1, vec![Instruction::Halt], Vec::new()),
8816 generator_function(
8817 0,
8818 3,
8819 vec![
8820 Instruction::LoadConst {
8821 dst: reg(0),
8822 constant: cid(0),
8823 },
8824 Instruction::Throw { value: reg(0) },
8825 Instruction::Suspend {
8826 dst: reg(2),
8827 src: reg(1),
8828 resume: pc(3),
8829 },
8830 Instruction::Return { value: reg(2) },
8831 ],
8832 vec![ExceptionHandler {
8833 start: pc(1),
8834 end: pc(2),
8835 handler: pc(2),
8836 catch_register: reg(1),
8837 }],
8838 ),
8839 ],
8840 );
8841 let mut host = TestHost;
8842 let mut machine = Machine::new(&program, &mut host, Limits::default());
8843 machine.frames.clear();
8844 machine.live_registers = 0;
8845 let callable = generator_callable(&mut machine, 1);
8846 let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8847
8848 assert_eq!(
8849 generator_next(&mut machine, generator, Value::UNDEFINED).unwrap(),
8850 (Value::int32(7), false),
8851 );
8852 assert_eq!(
8853 generator_next(&mut machine, generator, Value::int32(9)).unwrap(),
8854 (Value::int32(9), true),
8855 );
8856 }
8857
8858 #[test]
8859 fn suspended_generator_registers_remain_charged() {
8860 let program = verified(
8861 vec![Constant::Int32(1)],
8862 vec![
8863 function(0, 1, vec![Instruction::Halt], Vec::new()),
8864 generator_function(
8865 0,
8866 3,
8867 vec![
8868 Instruction::LoadConst {
8869 dst: reg(0),
8870 constant: cid(0),
8871 },
8872 Instruction::Suspend {
8873 dst: reg(1),
8874 src: reg(0),
8875 resume: pc(2),
8876 },
8877 Instruction::Return { value: reg(1) },
8878 ],
8879 Vec::new(),
8880 ),
8881 ],
8882 );
8883 let mut host = TestHost;
8884 let mut machine = Machine::new(
8885 &program,
8886 &mut host,
8887 Limits {
8888 max_total_registers: 3,
8889 ..Limits::default()
8890 },
8891 );
8892 machine.frames.clear();
8893 machine.live_registers = 0;
8894 let callable = generator_callable(&mut machine, 1);
8895 let first = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8896 let second = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8897 assert_eq!(
8898 generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
8899 (Value::int32(1), false),
8900 );
8901 assert!(matches!(
8902 generator_next(&mut machine, second, Value::UNDEFINED),
8903 Err(EvalFailure::Runtime(
8904 RuntimeErrorKind::RegisterLimitExceeded { .. }
8905 ))
8906 ));
8907 assert_eq!(machine.live_registers, 3);
8908 assert_eq!(
8909 generator_next(&mut machine, first, Value::int32(4)).unwrap(),
8910 (Value::int32(4), true),
8911 );
8912 assert_eq!(machine.live_registers, 0);
8913 }
8914
8915 #[test]
8916 fn resumed_generator_call_depth_failure_releases_registers() {
8917 let program = verified(
8918 vec![Constant::Int32(1)],
8919 vec![
8920 function(0, 1, vec![Instruction::Halt], Vec::new()),
8921 generator_function(
8922 0,
8923 2,
8924 vec![
8925 Instruction::LoadConst {
8926 dst: reg(0),
8927 constant: cid(0),
8928 },
8929 Instruction::Suspend {
8930 dst: reg(1),
8931 src: reg(0),
8932 resume: pc(2),
8933 },
8934 Instruction::Return { value: reg(1) },
8935 ],
8936 Vec::new(),
8937 ),
8938 ],
8939 );
8940 let mut host = TestHost;
8941 let mut machine = Machine::new(
8942 &program,
8943 &mut host,
8944 Limits {
8945 max_total_registers: 2,
8946 ..Limits::default()
8947 },
8948 );
8949 machine.frames.clear();
8950 machine.live_registers = 0;
8951
8952 let callable = generator_callable(&mut machine, 1);
8953 let first = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8954
8955 assert_eq!(
8957 generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
8958 (Value::int32(1), false),
8959 );
8960 assert_eq!(machine.live_registers, 2);
8961
8962 machine.frames.push(Frame {
8965 module: ModuleId::new(0),
8966 function: 0,
8967 pc: 0,
8968 registers: Vec::new(),
8969 return_to: None,
8970 this_value: Value::UNDEFINED,
8971 new_target: Value::UNDEFINED,
8972 args: Vec::new(),
8973 arguments_object: None,
8974 });
8975 machine.limits.max_call_depth = machine.frames.len();
8976
8977 assert!(matches!(
8978 generator_next(&mut machine, first, Value::int32(7)),
8979 Err(EvalFailure::Runtime(
8980 RuntimeErrorKind::CallDepthExceeded { .. }
8981 ))
8982 ));
8983 assert_eq!(machine.live_registers, 0);
8984
8985 assert_eq!(
8987 generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
8988 (Value::UNDEFINED, true),
8989 );
8990
8991 machine.frames.pop();
8993 machine.limits.max_call_depth = Limits::default().max_call_depth;
8994
8995 let second = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8997 assert_eq!(
8998 generator_next(&mut machine, second, Value::UNDEFINED).unwrap(),
8999 (Value::int32(1), false),
9000 );
9001 assert_eq!(machine.live_registers, 2);
9002 assert_eq!(
9003 generator_next(&mut machine, second, Value::int32(9)).unwrap(),
9004 (Value::int32(9), true),
9005 );
9006 assert_eq!(machine.live_registers, 0);
9007 }
9008 #[test]
9009 fn array_extend_consumes_generator_through_sync_iterator_protocol() {
9010 let program = verified(
9011 vec![Constant::Int32(1), Constant::Int32(2)],
9012 vec![
9013 function(0, 1, vec![Instruction::Halt], Vec::new()),
9014 generator_function(
9015 0,
9016 3,
9017 vec![
9018 Instruction::LoadConst {
9019 dst: reg(0),
9020 constant: cid(0),
9021 },
9022 Instruction::Suspend {
9023 dst: reg(2),
9024 src: reg(0),
9025 resume: pc(2),
9026 },
9027 Instruction::LoadConst {
9028 dst: reg(1),
9029 constant: cid(1),
9030 },
9031 Instruction::Suspend {
9032 dst: reg(2),
9033 src: reg(1),
9034 resume: pc(4),
9035 },
9036 Instruction::Return { value: reg(2) },
9037 ],
9038 Vec::new(),
9039 ),
9040 ],
9041 );
9042 let mut host = TestHost;
9043 let mut machine = Machine::new(&program, &mut host, Limits::default());
9044 machine.frames.clear();
9045 machine.live_registers = 0;
9046 let callable = generator_callable(&mut machine, 1);
9047 let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
9048 let array = machine
9049 .allocate(HeapEntry::Array {
9050 elements: Vec::new(),
9051 properties: PropertyMap::default(),
9052 prototype: Some(machine.intrinsics.array_prototype),
9053 extensible: true,
9054 length_writable: true,
9055 })
9056 .unwrap();
9057
9058 machine.array_extend(array, generator).unwrap();
9059 assert_eq!(
9060 machine.array_elements(array).unwrap(),
9061 Some(vec![Value::int32(1), Value::int32(2)]),
9062 );
9063 assert_eq!(machine.live_registers, 0);
9064 }
9065
9066 #[test]
9067 fn runtime_callback_without_interpreter_caller_propagates_throw() {
9068 let program = verified(
9069 Vec::new(),
9070 vec![
9071 function(0, 1, vec![Instruction::Halt], Vec::new()),
9072 function(1, 1, vec![Instruction::Throw { value: reg(0) }], Vec::new()),
9073 ],
9074 );
9075 let mut host = TestHost;
9076 let mut machine = Machine::new(&program, &mut host, Limits::default());
9077 machine.frames.clear();
9078 machine.live_registers = 0;
9079 let callee = machine
9080 .allocate(HeapEntry::Function {
9081 module: ModuleId::new(0),
9082 function: FunctionId::new(1),
9083 captures: Vec::new(),
9084 properties: PropertyMap::default(),
9085 prototype: Some(machine.intrinsics.function_prototype),
9086 extensible: true,
9087 })
9088 .unwrap();
9089 let thrown = Value::int32(7);
9090
9091 assert!(matches!(
9092 machine.call_value(callee, Value::UNDEFINED, &[thrown]),
9093 Err(EvalFailure::ThrowValue(value)) if value == thrown
9094 ));
9095 }
9096
9097 #[test]
9098 fn runtime_callback_failure_releases_root_frame() {
9099 let program = verified(
9100 Vec::new(),
9101 vec![
9102 function(0, 1, vec![Instruction::Halt], Vec::new()),
9103 function(
9104 1,
9105 1,
9106 vec![Instruction::Return { value: reg(0) }],
9107 Vec::new(),
9108 ),
9109 ],
9110 );
9111 let mut host = TestHost;
9112 let mut machine = Machine::new(&program, &mut host, Limits::default());
9113 machine.frames.clear();
9114 machine.live_registers = 0;
9115 let callee = machine
9116 .allocate(HeapEntry::Function {
9117 module: ModuleId::new(0),
9118 function: FunctionId::new(1),
9119 captures: Vec::new(),
9120 properties: PropertyMap::default(),
9121 prototype: Some(machine.intrinsics.function_prototype),
9122 extensible: true,
9123 })
9124 .unwrap();
9125 machine.fuel = 0;
9126
9127 assert!(matches!(
9128 machine.call_value(callee, Value::UNDEFINED, &[Value::int32(7)]),
9129 Err(EvalFailure::Runtime(RuntimeErrorKind::FuelExhausted { .. }))
9130 ));
9131 assert!(machine.frames.is_empty());
9132 assert_eq!(machine.live_registers, 0);
9133
9134 machine.fuel = 1;
9135 assert!(matches!(
9136 machine.call_value(callee, Value::UNDEFINED, &[Value::int32(7)]),
9137 Ok(value) if value == Value::int32(7)
9138 ));
9139 }
9140
9141 #[test]
9142 fn object_values_have_stable_distinct_heap_identity() {
9143 let module = verified(
9144 vec![],
9145 vec![function(
9146 0,
9147 5,
9148 vec![
9149 Instruction::CreateObject { dst: reg(0) },
9150 Instruction::CreateObject { dst: reg(1) },
9151 Instruction::Binary {
9152 dst: reg(2),
9153 op: BinaryOp::StrictEqual,
9154 left: reg(0),
9155 right: reg(1),
9156 },
9157 Instruction::Move {
9158 dst: reg(3),
9159 src: reg(0),
9160 },
9161 Instruction::Binary {
9162 dst: reg(4),
9163 op: BinaryOp::StrictEqual,
9164 left: reg(0),
9165 right: reg(3),
9166 },
9167 Instruction::Return { value: reg(4) },
9168 ],
9169 vec![],
9170 )],
9171 );
9172 let execution = run_ok(&module);
9173 assert_eq!(execution.entry_registers[2], Value::FALSE);
9174 assert_eq!(execution.value, Value::TRUE);
9175 }
9176
9177 #[test]
9178 fn addition_coerces_objects_left_to_right_and_interpolates_errors() {
9179 let module = verified(
9180 vec![
9181 Constant::String(EcmaString::from_utf8("L")),
9182 Constant::String(EcmaString::from_utf8("additionOrder")),
9183 Constant::String(EcmaString::from_utf8("message")),
9184 ],
9185 vec![
9186 function(0, 1, vec![Instruction::Halt], Vec::new()),
9187 function(
9188 0,
9189 1,
9190 vec![
9191 Instruction::LoadConst {
9192 dst: reg(0),
9193 constant: cid(0),
9194 },
9195 Instruction::StoreGlobal {
9196 name: cid(1),
9197 value: reg(0),
9198 },
9199 Instruction::Return { value: reg(0) },
9200 ],
9201 Vec::new(),
9202 ),
9203 function(
9204 0,
9205 1,
9206 vec![
9207 Instruction::LoadGlobal {
9208 dst: reg(0),
9209 name: cid(1),
9210 },
9211 Instruction::Return { value: reg(0) },
9212 ],
9213 Vec::new(),
9214 ),
9215 ],
9216 );
9217 let mut host = TestHost;
9218 let mut machine = Machine::new(&module, &mut host, Limits::default());
9219 machine.frames.clear();
9220 machine.live_registers = 0;
9221 let left = machine
9222 .allocate(HeapEntry::Object {
9223 properties: PropertyMap::default(),
9224 prototype: Some(machine.intrinsics.object_prototype),
9225 extensible: true,
9226 boxed_primitive: None,
9227 })
9228 .unwrap();
9229 let right = machine
9230 .allocate(HeapEntry::Object {
9231 properties: PropertyMap::default(),
9232 prototype: Some(machine.intrinsics.object_prototype),
9233 extensible: true,
9234 boxed_primitive: None,
9235 })
9236 .unwrap();
9237 let left_value_of = machine
9238 .allocate(HeapEntry::Function {
9239 module: ModuleId::new(0),
9240 function: FunctionId::new(1),
9241 captures: Vec::new(),
9242 properties: PropertyMap::default(),
9243 prototype: Some(machine.intrinsics.function_prototype),
9244 extensible: true,
9245 })
9246 .unwrap();
9247 let right_value_of = machine
9248 .allocate(HeapEntry::Function {
9249 module: ModuleId::new(0),
9250 function: FunctionId::new(2),
9251 captures: Vec::new(),
9252 properties: PropertyMap::default(),
9253 prototype: Some(machine.intrinsics.function_prototype),
9254 extensible: true,
9255 })
9256 .unwrap();
9257 machine
9258 .set_data_property(left, "valueOf", left_value_of)
9259 .unwrap();
9260 machine
9261 .set_data_property(right, "valueOf", right_value_of)
9262 .unwrap();
9263 let coerced = machine.add(left, right).unwrap();
9264 assert!(
9265 machine
9266 .string_value(coerced)
9267 .is_some_and(|text| text.eq_ascii("LL"))
9268 );
9269
9270 let error_constructor = machine.intrinsics.global("Error").unwrap();
9271 let message = machine
9272 .allocate(HeapEntry::String(EcmaString::from_utf8("message")))
9273 .unwrap();
9274 let error = machine
9275 .call_value(error_constructor, Value::UNDEFINED, &[message])
9276 .unwrap();
9277 let empty = machine
9278 .allocate(HeapEntry::String(EcmaString::default()))
9279 .unwrap();
9280 let interpolated = machine.add(empty, error).unwrap();
9281 assert!(
9282 machine
9283 .string_value(interpolated)
9284 .is_some_and(|text| text.eq_ascii("Error: message"))
9285 );
9286
9287 let date_constructor = machine.intrinsics.global("Date").unwrap();
9288 let date_prototype = machine
9289 .get_named_property(date_constructor, "prototype")
9290 .unwrap();
9291 let date = machine
9292 .allocate(HeapEntry::Date {
9293 time: 0.0,
9294 properties: PropertyMap::default(),
9295 prototype: Some(date_prototype),
9296 extensible: true,
9297 })
9298 .unwrap();
9299 machine
9300 .set_data_property(date, "toString", left_value_of)
9301 .unwrap();
9302 let date_text = machine.add(date, empty).unwrap();
9303 assert!(
9304 machine
9305 .string_value(date_text)
9306 .is_some_and(|text| text.eq_ascii("L"))
9307 );
9308 }
9309
9310 #[test]
9311 fn computed_member_access_uses_dynamic_register_key() {
9312 let module = verified(
9314 vec![
9315 Constant::String(EcmaString::from_utf8("a")),
9316 Constant::String(EcmaString::from_utf8("b")),
9317 Constant::Int32(7),
9318 ],
9319 vec![function(
9320 0,
9321 6,
9322 vec![
9323 Instruction::LoadConst {
9324 dst: reg(1),
9325 constant: cid(0),
9326 },
9327 Instruction::LoadConst {
9328 dst: reg(2),
9329 constant: cid(1),
9330 },
9331 Instruction::Binary {
9332 dst: reg(3),
9333 op: BinaryOp::Add,
9334 left: reg(1),
9335 right: reg(2),
9336 },
9337 Instruction::CreateObject { dst: reg(0) },
9338 Instruction::LoadConst {
9339 dst: reg(4),
9340 constant: cid(2),
9341 },
9342 Instruction::SetProperty {
9343 object: reg(0),
9344 key: reg(3),
9345 value: reg(4),
9346 },
9347 Instruction::GetProperty {
9348 dst: reg(5),
9349 object: reg(0),
9350 key: reg(3),
9351 },
9352 Instruction::Return { value: reg(5) },
9353 ],
9354 vec![],
9355 )],
9356 );
9357 assert_eq!(run_ok(&module).value, Value::int32(7));
9358 }
9359
9360 #[test]
9361 fn property_delete_and_array_holes_are_real_mutations() {
9362 let module = verified(
9363 vec![
9364 Constant::String(EcmaString::from_utf8("0")),
9365 Constant::Int32(5),
9366 ],
9367 vec![function(
9368 0,
9369 5,
9370 vec![
9371 Instruction::CreateArray { dst: reg(0) },
9372 Instruction::LoadConst {
9373 dst: reg(1),
9374 constant: cid(0),
9375 },
9376 Instruction::LoadConst {
9377 dst: reg(4),
9378 constant: cid(1),
9379 },
9380 Instruction::SetProperty {
9381 object: reg(0),
9382 key: reg(1),
9383 value: reg(4),
9384 },
9385 Instruction::GetProperty {
9386 dst: reg(2),
9387 object: reg(0),
9388 key: reg(1),
9389 },
9390 Instruction::DeleteProperty {
9391 dst: reg(3),
9392 object: reg(0),
9393 key: reg(1),
9394 },
9395 Instruction::GetProperty {
9396 dst: reg(4),
9397 object: reg(0),
9398 key: reg(1),
9399 },
9400 Instruction::Return { value: reg(3) },
9401 ],
9402 vec![],
9403 )],
9404 );
9405 let execution = run_ok(&module);
9406 assert_eq!(execution.entry_registers[2], Value::int32(5));
9407 assert_eq!(execution.entry_registers[4], Value::UNDEFINED);
9408 assert_eq!(execution.value, Value::TRUE);
9409 }
9410
9411 #[test]
9412 fn closure_captures_seed_leading_registers_before_parameters() {
9413 let entry = function(
9415 0,
9416 3,
9417 vec![
9418 Instruction::CreateArray { dst: reg(0) },
9419 Instruction::LoadConst {
9420 dst: reg(1),
9421 constant: cid(0),
9422 },
9423 Instruction::ArrayPush {
9424 array: reg(0),
9425 value: reg(1),
9426 },
9427 Instruction::CreateClosure {
9428 dst: reg(2),
9429 function: FunctionId::new(1),
9430 captures: reg(0),
9431 },
9432 Instruction::CreateArray { dst: reg(0) },
9434 Instruction::LoadConst {
9435 dst: reg(1),
9436 constant: cid(1),
9437 },
9438 Instruction::ArrayPush {
9439 array: reg(0),
9440 value: reg(1),
9441 },
9442 Instruction::LoadConst {
9443 dst: reg(1),
9444 constant: cid(2),
9445 },
9446 Instruction::Call {
9447 dst: reg(1),
9448 callee: reg(2),
9449 this_value: reg(1),
9450 arguments: reg(0),
9451 },
9452 Instruction::Return { value: reg(1) },
9453 ],
9454 vec![],
9455 );
9456 let callee = closure_function(
9458 1,
9459 1,
9460 3,
9461 vec![
9462 Instruction::Binary {
9463 dst: reg(2),
9464 op: BinaryOp::Add,
9465 left: reg(0),
9466 right: reg(1),
9467 },
9468 Instruction::Return { value: reg(2) },
9469 ],
9470 );
9471 let module = verified(
9472 vec![Constant::Int32(42), Constant::Int32(7), Constant::Undefined],
9473 vec![entry, callee],
9474 );
9475 assert_eq!(run_ok(&module).value, Value::int32(49));
9476 }
9477
9478 #[test]
9479 fn calls_scale_past_fixed_window_via_arguments_array() {
9480 let mut code = vec![Instruction::CreateArray { dst: reg(0) }];
9483 code.push(Instruction::LoadConst {
9484 dst: reg(1),
9485 constant: cid(0),
9486 });
9487 for _ in 0..500 {
9488 code.push(Instruction::ArrayPush {
9489 array: reg(0),
9490 value: reg(1),
9491 });
9492 }
9493 code.push(Instruction::CreateClosure {
9494 dst: reg(2),
9495 function: FunctionId::new(1),
9496 captures: reg(3),
9497 });
9498 let mut prelude = vec![Instruction::CreateArray { dst: reg(3) }];
9502 prelude.append(&mut code);
9503 let mut code = prelude;
9504 code.push(Instruction::LoadConst {
9505 dst: reg(1),
9506 constant: cid(1),
9507 });
9508 code.push(Instruction::Call {
9509 dst: reg(1),
9510 callee: reg(2),
9511 this_value: reg(1),
9512 arguments: reg(0),
9513 });
9514 code.push(Instruction::Return { value: reg(1) });
9515
9516 let entry = function(0, 4, code, vec![]);
9517 let callee = function(
9518 0,
9519 2,
9520 vec![
9521 Instruction::LoadArguments { dst: reg(0) },
9522 Instruction::LoadConst {
9523 dst: reg(1),
9524 constant: cid(2),
9525 },
9526 Instruction::GetProperty {
9527 dst: reg(0),
9528 object: reg(0),
9529 key: reg(1),
9530 },
9531 Instruction::Return { value: reg(0) },
9532 ],
9533 vec![],
9534 );
9535 let module = verified(
9536 vec![
9537 Constant::Int32(1),
9538 Constant::Undefined,
9539 Constant::String(EcmaString::from_utf8("length")),
9540 ],
9541 vec![entry, callee],
9542 );
9543 assert_eq!(run_ok(&module).value, Value::int32(500));
9544 }
9545
9546 #[test]
9547 fn array_extend_spreads_iterable_elements() {
9548 let entry = function(
9550 0,
9551 4,
9552 vec![
9553 Instruction::CreateArray { dst: reg(0) },
9554 Instruction::LoadConst {
9555 dst: reg(1),
9556 constant: cid(0),
9557 },
9558 Instruction::ArrayPush {
9559 array: reg(0),
9560 value: reg(1),
9561 },
9562 Instruction::CreateArray { dst: reg(2) },
9564 Instruction::LoadConst {
9565 dst: reg(1),
9566 constant: cid(1),
9567 },
9568 Instruction::ArrayPush {
9569 array: reg(2),
9570 value: reg(1),
9571 },
9572 Instruction::LoadConst {
9573 dst: reg(1),
9574 constant: cid(2),
9575 },
9576 Instruction::ArrayPush {
9577 array: reg(2),
9578 value: reg(1),
9579 },
9580 Instruction::ArrayExtend {
9581 array: reg(0),
9582 iterable: reg(2),
9583 },
9584 Instruction::LoadConst {
9585 dst: reg(3),
9586 constant: cid(3),
9587 },
9588 Instruction::GetProperty {
9589 dst: reg(0),
9590 object: reg(0),
9591 key: reg(3),
9592 },
9593 Instruction::Return { value: reg(0) },
9594 ],
9595 vec![],
9596 );
9597 let module = verified(
9598 vec![
9599 Constant::Int32(1),
9600 Constant::Int32(2),
9601 Constant::Int32(3),
9602 Constant::String(EcmaString::from_utf8("length")),
9603 ],
9604 vec![entry],
9605 );
9606 assert_eq!(run_ok(&module).value, Value::int32(3));
9607 }
9608
9609 #[test]
9610 fn array_extend_uses_sync_protocol_for_set_and_rejects_plain_object() {
9611 let module = verified(
9612 Vec::new(),
9613 vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
9614 );
9615 let mut host = TestHost;
9616 let mut machine = Machine::new(&module, &mut host, Limits::default());
9617 let set_constructor = machine.intrinsics.global("Set").unwrap();
9618 let set_prototype = machine
9619 .get_named_property(set_constructor, "prototype")
9620 .unwrap();
9621 let set = machine
9622 .allocate(HeapEntry::Collection {
9623 entries: vec![CollectionEntry {
9624 order: 0,
9625 key: Value::int32(7),
9626 value: Value::int32(7),
9627 }],
9628 next_order: 1,
9629 properties: PropertyMap::default(),
9630 prototype: Some(set_prototype),
9631 extensible: true,
9632 })
9633 .unwrap();
9634 let target = machine
9635 .allocate(HeapEntry::Array {
9636 elements: Vec::new(),
9637 properties: PropertyMap::default(),
9638 prototype: Some(machine.intrinsics.array_prototype),
9639 extensible: true,
9640 length_writable: true,
9641 })
9642 .unwrap();
9643
9644 machine.array_extend(target, set).unwrap();
9645 assert_eq!(
9646 machine.array_elements(target).unwrap(),
9647 Some(vec![Value::int32(7)])
9648 );
9649
9650 let plain_object = machine
9651 .allocate(HeapEntry::Object {
9652 properties: PropertyMap::default(),
9653 prototype: Some(machine.intrinsics.object_prototype),
9654 boxed_primitive: None,
9655 extensible: true,
9656 })
9657 .unwrap();
9658 assert!(matches!(
9659 machine.array_extend(target, plain_object),
9660 Err(EvalFailure::Throw(ThrowOrigin::TypeError {
9661 operation: "value is not iterable"
9662 }))
9663 ));
9664 }
9665
9666 #[test]
9667 fn sync_iterator_uses_symbol_method_and_caches_next() {
9668 fn iterator_identity<H: Host>(
9669 _machine: &mut Machine<'_, H>,
9670 this: Value,
9671 _args: &[Value],
9672 _constructing: bool,
9673 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9674 Ok(intrinsics::BuiltinOutcome::Value(this))
9675 }
9676
9677 fn next_getter<H: Host>(
9678 machine: &mut Machine<'_, H>,
9679 this: Value,
9680 _args: &[Value],
9681 _constructing: bool,
9682 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9683 let reads = machine.get_named_property(this, "nextReads")?;
9684 let reads = if reads == Value::int32(0) { 1 } else { 2 };
9685 machine.set_data_property(this, "nextReads", Value::int32(reads))?;
9686 Ok(intrinsics::BuiltinOutcome::Value(
9687 machine.get_named_property(this, "nextFunction")?,
9688 ))
9689 }
9690
9691 fn next_result<H: Host>(
9692 machine: &mut Machine<'_, H>,
9693 this: Value,
9694 _args: &[Value],
9695 _constructing: bool,
9696 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9697 Ok(intrinsics::BuiltinOutcome::Value(
9698 machine.get_named_property(this, "result")?,
9699 ))
9700 }
9701
9702 fn done_getter<H: Host>(
9703 machine: &mut Machine<'_, H>,
9704 this: Value,
9705 _args: &[Value],
9706 _constructing: bool,
9707 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9708 machine.set_data_property(this, "order", Value::int32(1))?;
9709 Ok(intrinsics::BuiltinOutcome::Value(Value::FALSE))
9710 }
9711
9712 fn value_getter<H: Host>(
9713 machine: &mut Machine<'_, H>,
9714 this: Value,
9715 _args: &[Value],
9716 _constructing: bool,
9717 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9718 if machine.get_named_property(this, "order")? != Value::int32(1) {
9719 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
9720 operation: "iterator value read before done",
9721 }));
9722 }
9723 Ok(intrinsics::BuiltinOutcome::Value(Value::int32(42)))
9724 }
9725
9726 let module = verified(
9727 Vec::new(),
9728 vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
9729 );
9730 let mut host = TestHost;
9731 let mut machine = Machine::new(&module, &mut host, Limits::default());
9732 let mut install = |name, handler| {
9733 let id = machine
9734 .intrinsics
9735 .builtins
9736 .register(intrinsics::BuiltinDef {
9737 name,
9738 length: 0,
9739 handler,
9740 });
9741 intrinsics::native_function(&mut machine.heap, id, name, 0)
9742 };
9743 let iterator_identity = install(
9744 "[Symbol.iterator]",
9745 iterator_identity::<TestHost> as intrinsics::BuiltinHandler<TestHost>,
9746 );
9747 let next_getter = install("get next", next_getter::<TestHost>);
9748 let next_result = install("next", next_result::<TestHost>);
9749 let done_getter = install("get done", done_getter::<TestHost>);
9750 let value_getter = install("get value", value_getter::<TestHost>);
9751 let object_prototype = machine.intrinsics.object_prototype;
9752 let result = machine
9753 .allocate(HeapEntry::Object {
9754 properties: {
9755 let mut properties = PropertyMap::default();
9756 for (key, property) in [
9757 (
9758 PropertyKey::Named(EcmaString::from_utf8("order")),
9759 Property::Data {
9760 value: Value::int32(0),
9761 writable: true,
9762 enumerable: true,
9763 configurable: true,
9764 },
9765 ),
9766 (
9767 PropertyKey::Named(EcmaString::from_utf8("done")),
9768 Property::Accessor {
9769 getter: Some(done_getter),
9770 setter: None,
9771 enumerable: true,
9772 configurable: true,
9773 },
9774 ),
9775 (
9776 PropertyKey::Named(EcmaString::from_utf8("value")),
9777 Property::Accessor {
9778 getter: Some(value_getter),
9779 setter: None,
9780 enumerable: true,
9781 configurable: true,
9782 },
9783 ),
9784 ] {
9785 properties.insert(key, property);
9786 }
9787 properties
9788 },
9789 prototype: Some(object_prototype),
9790 boxed_primitive: None,
9791 extensible: true,
9792 })
9793 .unwrap();
9794 let iterator_symbol = machine.intrinsics.builtins.symbol_iterator();
9795 let iterator_key = machine.to_property_key(iterator_symbol).unwrap();
9796 let source = machine
9797 .allocate(HeapEntry::Object {
9798 properties: {
9799 let mut properties = PropertyMap::default();
9800 for (key, property) in [
9801 (
9802 iterator_key,
9803 Property::Data {
9804 value: iterator_identity,
9805 writable: true,
9806 enumerable: false,
9807 configurable: true,
9808 },
9809 ),
9810 (
9811 PropertyKey::Named(EcmaString::from_utf8("next")),
9812 Property::Accessor {
9813 getter: Some(next_getter),
9814 setter: None,
9815 enumerable: false,
9816 configurable: true,
9817 },
9818 ),
9819 (
9820 PropertyKey::Named(EcmaString::from_utf8("nextReads")),
9821 Property::Data {
9822 value: Value::int32(0),
9823 writable: true,
9824 enumerable: true,
9825 configurable: true,
9826 },
9827 ),
9828 (
9829 PropertyKey::Named(EcmaString::from_utf8("nextFunction")),
9830 Property::Data {
9831 value: next_result,
9832 writable: true,
9833 enumerable: true,
9834 configurable: true,
9835 },
9836 ),
9837 (
9838 PropertyKey::Named(EcmaString::from_utf8("result")),
9839 Property::Data {
9840 value: result,
9841 writable: true,
9842 enumerable: true,
9843 configurable: true,
9844 },
9845 ),
9846 ] {
9847 properties.insert(key, property);
9848 }
9849 properties
9850 },
9851 prototype: Some(object_prototype),
9852 boxed_primitive: None,
9853 extensible: true,
9854 })
9855 .unwrap();
9856
9857 let iterator = machine.create_iterator(source, IteratorKind::Sync).unwrap();
9858 assert_eq!(
9859 machine.iterator_next(iterator).unwrap(),
9860 (false, Value::int32(42))
9861 );
9862 assert_eq!(
9863 machine.iterator_next(iterator).unwrap(),
9864 (false, Value::int32(42))
9865 );
9866 assert_eq!(
9867 machine.get_named_property(source, "nextReads").unwrap(),
9868 Value::int32(1)
9869 );
9870
9871 let mut completed_properties = PropertyMap::default();
9872 completed_properties.insert(
9873 PropertyKey::Named(EcmaString::from_utf8("done")),
9874 Property::Data {
9875 value: Value::TRUE,
9876 writable: true,
9877 enumerable: true,
9878 configurable: true,
9879 },
9880 );
9881 completed_properties.insert(
9882 PropertyKey::Named(EcmaString::from_utf8("value")),
9883 Property::Accessor {
9884 getter: Some(value_getter),
9885 setter: None,
9886 enumerable: true,
9887 configurable: true,
9888 },
9889 );
9890 let completed = machine
9891 .allocate(HeapEntry::Object {
9892 properties: completed_properties,
9893 prototype: Some(object_prototype),
9894 boxed_primitive: None,
9895 extensible: true,
9896 })
9897 .unwrap();
9898 machine
9899 .set_data_property(source, "result", completed)
9900 .unwrap();
9901 assert_eq!(
9902 machine.iterator_next(iterator).unwrap(),
9903 (true, Value::UNDEFINED)
9904 );
9905
9906 machine
9907 .delete_property(source, &PropertyKey::Named(EcmaString::from_utf8("next")))
9908 .unwrap();
9909 machine
9910 .set_data_property(source, "next", Value::int32(1))
9911 .unwrap();
9912 let invalid_next = machine.create_iterator(source, IteratorKind::Sync).unwrap();
9913 assert!(matches!(
9914 machine.iterator_next(invalid_next),
9915 Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
9916 ));
9917 }
9918
9919 #[test]
9920 fn object_spread_copies_own_properties() {
9921 let key = |c: u32| Instruction::LoadConst {
9923 dst: reg(3),
9924 constant: cid(c),
9925 };
9926 let module = verified(
9927 vec![
9928 Constant::String(EcmaString::from_utf8("x")),
9929 Constant::Int32(9),
9930 ],
9931 vec![function(
9932 0,
9933 4,
9934 vec![
9935 Instruction::CreateObject { dst: reg(0) },
9936 key(0),
9937 Instruction::LoadConst {
9938 dst: reg(2),
9939 constant: cid(1),
9940 },
9941 Instruction::SetProperty {
9942 object: reg(0),
9943 key: reg(3),
9944 value: reg(2),
9945 },
9946 Instruction::CreateObject { dst: reg(1) },
9947 Instruction::ObjectSpread {
9948 target: reg(1),
9949 source: reg(0),
9950 },
9951 key(0),
9952 Instruction::GetProperty {
9953 dst: reg(2),
9954 object: reg(1),
9955 key: reg(3),
9956 },
9957 Instruction::Return { value: reg(2) },
9958 ],
9959 vec![],
9960 )],
9961 );
9962 assert_eq!(run_ok(&module).value, Value::int32(9));
9963 }
9964
9965 #[test]
9966 fn object_spread_copies_enumerable_symbol_properties() {
9967 let module = verified(
9968 Vec::new(),
9969 vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
9970 );
9971 let mut host = TestHost;
9972 let mut machine = Machine::new(&module, &mut host, Limits::default());
9973 let prototype = machine.intrinsics.object_prototype;
9974 let object = |machine: &mut Machine<'_, TestHost>| {
9975 machine
9976 .allocate(HeapEntry::Object {
9977 properties: PropertyMap::default(),
9978 prototype: Some(prototype),
9979 boxed_primitive: None,
9980 extensible: true,
9981 })
9982 .unwrap()
9983 };
9984 let source = object(&mut machine);
9985 let target = object(&mut machine);
9986 let symbol = machine
9987 .allocate(HeapEntry::Symbol {
9988 description: EcmaString::from_utf8("key"),
9989 })
9990 .unwrap();
9991 let key = machine.to_property_key(symbol).unwrap();
9992 machine
9993 .set_data_property_key(source, key.clone(), Value::int32(42))
9994 .unwrap();
9995
9996 machine.object_spread(target, source).unwrap();
9997
9998 assert_eq!(
9999 machine.get_property_key(target, &key).unwrap(),
10000 Value::int32(42)
10001 );
10002 }
10003
10004 #[test]
10005 fn object_spread_rechecks_descriptors_after_getters() {
10006 fn delete_next<H: Host>(
10007 machine: &mut Machine<'_, H>,
10008 this: Value,
10009 _args: &[Value],
10010 _constructing: bool,
10011 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
10012 machine.delete_property(this, &PropertyKey::Named(EcmaString::from_utf8("next")))?;
10013 Ok(intrinsics::BuiltinOutcome::Value(Value::int32(1)))
10014 }
10015
10016 let module = verified(
10017 Vec::new(),
10018 vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
10019 );
10020 let mut host = TestHost;
10021 let mut machine = Machine::new(&module, &mut host, Limits::default());
10022 let getter_id = machine
10023 .intrinsics
10024 .builtins
10025 .register(intrinsics::BuiltinDef {
10026 name: "delete next",
10027 length: 0,
10028 handler: delete_next::<TestHost>,
10029 });
10030 let getter = intrinsics::native_function(&mut machine.heap, getter_id, "delete next", 0);
10031 let first = PropertyKey::Named(EcmaString::from_utf8("first"));
10032 let next = PropertyKey::Named(EcmaString::from_utf8("next"));
10033 let mut source_properties = PropertyMap::default();
10034 source_properties.insert(
10035 first.clone(),
10036 Property::Accessor {
10037 getter: Some(getter),
10038 setter: None,
10039 enumerable: true,
10040 configurable: true,
10041 },
10042 );
10043 source_properties.insert(
10044 next.clone(),
10045 Property::Data {
10046 value: Value::int32(2),
10047 writable: true,
10048 enumerable: true,
10049 configurable: true,
10050 },
10051 );
10052 let prototype = machine.intrinsics.object_prototype;
10053 let source = machine
10054 .allocate(HeapEntry::Object {
10055 properties: source_properties,
10056 prototype: Some(prototype),
10057 boxed_primitive: None,
10058 extensible: true,
10059 })
10060 .unwrap();
10061 let target = machine
10062 .allocate(HeapEntry::Object {
10063 properties: PropertyMap::default(),
10064 prototype: Some(prototype),
10065 boxed_primitive: None,
10066 extensible: true,
10067 })
10068 .unwrap();
10069
10070 machine.object_spread(target, source).unwrap();
10071
10072 assert_eq!(
10073 machine.get_property_key(target, &first).unwrap(),
10074 Value::int32(1)
10075 );
10076 assert!(!machine.has_own_property_key(target, &next).unwrap());
10077 }
10078
10079 #[test]
10080 fn private_names_have_distinct_identity_and_are_gettable() {
10081 let module = verified(
10083 vec![
10084 Constant::String(EcmaString::from_utf8("x")),
10085 Constant::Int32(1),
10086 Constant::Int32(2),
10087 ],
10088 vec![function(
10089 0,
10090 6,
10091 vec![
10092 Instruction::CreateObject { dst: reg(0) },
10093 Instruction::CreatePrivateName {
10094 dst: reg(1),
10095 description: cid(0),
10096 },
10097 Instruction::CreatePrivateName {
10098 dst: reg(2),
10099 description: cid(0),
10100 },
10101 Instruction::LoadConst {
10102 dst: reg(3),
10103 constant: cid(1),
10104 },
10105 Instruction::SetProperty {
10106 object: reg(0),
10107 key: reg(1),
10108 value: reg(3),
10109 },
10110 Instruction::LoadConst {
10111 dst: reg(3),
10112 constant: cid(2),
10113 },
10114 Instruction::SetProperty {
10115 object: reg(0),
10116 key: reg(2),
10117 value: reg(3),
10118 },
10119 Instruction::GetProperty {
10121 dst: reg(4),
10122 object: reg(0),
10123 key: reg(1),
10124 },
10125 Instruction::GetProperty {
10126 dst: reg(5),
10127 object: reg(0),
10128 key: reg(2),
10129 },
10130 Instruction::Binary {
10132 dst: reg(3),
10133 op: BinaryOp::StrictEqual,
10134 left: reg(1),
10135 right: reg(2),
10136 },
10137 Instruction::Return { value: reg(4) },
10138 ],
10139 vec![],
10140 )],
10141 );
10142 let execution = run_ok(&module);
10143 assert_eq!(execution.value, Value::int32(1));
10144 assert_eq!(execution.entry_registers[5], Value::int32(2));
10145 assert_eq!(execution.entry_registers[3], Value::FALSE);
10146 }
10147
10148 #[test]
10149 fn accessor_getter_is_invoked_on_property_read() {
10150 let entry = function(
10152 0,
10153 4,
10154 vec![
10155 Instruction::CreateObject { dst: reg(0) },
10156 Instruction::CreateArray { dst: reg(3) },
10157 Instruction::CreateClosure {
10158 dst: reg(1),
10159 function: FunctionId::new(1),
10160 captures: reg(3),
10161 },
10162 Instruction::LoadConst {
10163 dst: reg(2),
10164 constant: cid(0),
10165 },
10166 Instruction::DefineAccessor {
10167 object: reg(0),
10168 key: reg(2),
10169 accessor: reg(1),
10170 kind: AccessorKind::Getter,
10171 },
10172 Instruction::GetProperty {
10173 dst: reg(1),
10174 object: reg(0),
10175 key: reg(2),
10176 },
10177 Instruction::Return { value: reg(1) },
10178 ],
10179 vec![],
10180 );
10181 let getter = function(
10182 0,
10183 1,
10184 vec![
10185 Instruction::LoadConst {
10186 dst: reg(0),
10187 constant: cid(1),
10188 },
10189 Instruction::Return { value: reg(0) },
10190 ],
10191 vec![],
10192 );
10193 let module = verified(
10194 vec![
10195 Constant::String(EcmaString::from_utf8("g")),
10196 Constant::Int32(99),
10197 ],
10198 vec![entry, getter],
10199 );
10200 assert_eq!(run_ok(&module).value, Value::int32(99));
10201 }
10202
10203 #[test]
10204 fn prototype_chain_lookup_and_instanceof() {
10205 let entry = function(
10208 0,
10209 6,
10210 vec![
10211 Instruction::CreateObject { dst: reg(0) },
10213 Instruction::LoadConst {
10214 dst: reg(1),
10215 constant: cid(0),
10216 },
10217 Instruction::LoadConst {
10218 dst: reg(2),
10219 constant: cid(1),
10220 },
10221 Instruction::SetProperty {
10222 object: reg(0),
10223 key: reg(1),
10224 value: reg(2),
10225 },
10226 Instruction::CreateArray { dst: reg(4) },
10228 Instruction::CreateClosure {
10229 dst: reg(3),
10230 function: FunctionId::new(1),
10231 captures: reg(4),
10232 },
10233 Instruction::LoadConst {
10235 dst: reg(1),
10236 constant: cid(2),
10237 },
10238 Instruction::SetProperty {
10239 object: reg(3),
10240 key: reg(1),
10241 value: reg(0),
10242 },
10243 Instruction::CreateArray { dst: reg(4) },
10245 Instruction::Construct {
10246 dst: reg(0),
10247 callee: reg(3),
10248 arguments: reg(4),
10249 },
10250 Instruction::LoadConst {
10252 dst: reg(1),
10253 constant: cid(0),
10254 },
10255 Instruction::GetProperty {
10256 dst: reg(2),
10257 object: reg(0),
10258 key: reg(1),
10259 },
10260 Instruction::Binary {
10262 dst: reg(5),
10263 op: BinaryOp::InstanceOf,
10264 left: reg(0),
10265 right: reg(3),
10266 },
10267 Instruction::Return { value: reg(2) },
10268 ],
10269 vec![],
10270 );
10271 let ctor = function(0, 1, vec![Instruction::Halt], vec![]);
10272 let module = verified(
10273 vec![
10274 Constant::String(EcmaString::from_utf8("m")),
10275 Constant::Int32(5),
10276 Constant::String(EcmaString::from_utf8("prototype")),
10277 ],
10278 vec![entry, ctor],
10279 );
10280 let execution = run_ok(&module);
10281 assert_eq!(execution.value, Value::int32(5));
10282 assert_eq!(execution.entry_registers[5], Value::TRUE);
10283 }
10284
10285 #[test]
10286 fn sync_iterator_walks_array_elements() {
10287 let entry = function(
10289 0,
10290 6,
10291 vec![
10292 Instruction::CreateArray { dst: reg(0) },
10293 Instruction::LoadConst {
10294 dst: reg(1),
10295 constant: cid(0),
10296 },
10297 Instruction::ArrayPush {
10298 array: reg(0),
10299 value: reg(1),
10300 },
10301 Instruction::LoadConst {
10302 dst: reg(1),
10303 constant: cid(1),
10304 },
10305 Instruction::ArrayPush {
10306 array: reg(0),
10307 value: reg(1),
10308 },
10309 Instruction::LoadConst {
10311 dst: reg(2),
10312 constant: cid(2),
10313 },
10314 Instruction::GetIterator {
10315 dst: reg(3),
10316 src: reg(0),
10317 kind: IteratorKind::Sync,
10318 },
10319 Instruction::IteratorNext {
10321 done: reg(4),
10322 value: reg(5),
10323 iterator: reg(3),
10324 },
10325 Instruction::JumpIfTrue {
10326 condition: reg(4),
10327 target: pc(11),
10328 },
10329 Instruction::Binary {
10330 dst: reg(2),
10331 op: BinaryOp::Add,
10332 left: reg(2),
10333 right: reg(5),
10334 },
10335 Instruction::Jump { target: pc(7) },
10336 Instruction::Return { value: reg(2) },
10338 ],
10339 vec![],
10340 );
10341 let module = verified(
10342 vec![Constant::Int32(10), Constant::Int32(20), Constant::Int32(0)],
10343 vec![entry],
10344 );
10345 assert_eq!(run_ok(&module).value, Value::int32(30));
10346 }
10347
10348 #[test]
10349 fn keys_iterator_enumerates_own_object_keys() {
10350 let entry = function(
10352 0,
10353 6,
10354 vec![
10355 Instruction::CreateObject { dst: reg(0) },
10356 Instruction::LoadConst {
10357 dst: reg(1),
10358 constant: cid(0),
10359 },
10360 Instruction::LoadConst {
10361 dst: reg(2),
10362 constant: cid(1),
10363 },
10364 Instruction::SetProperty {
10365 object: reg(0),
10366 key: reg(1),
10367 value: reg(2),
10368 },
10369 Instruction::GetIterator {
10370 dst: reg(3),
10371 src: reg(0),
10372 kind: IteratorKind::Keys,
10373 },
10374 Instruction::IteratorNext {
10375 done: reg(4),
10376 value: reg(5),
10377 iterator: reg(3),
10378 },
10379 Instruction::Return { value: reg(5) },
10380 ],
10381 vec![],
10382 );
10383 let module = verified(
10384 vec![
10385 Constant::String(EcmaString::from_utf8("a")),
10386 Constant::Int32(1),
10387 ],
10388 vec![entry],
10389 );
10390 let execution = run_ok(&module);
10391 let key = execution.value;
10393 assert_eq!(execution.entry_registers[4], Value::FALSE);
10396 assert_ne!(key, Value::UNDEFINED);
10397 }
10398
10399 #[test]
10400 fn async_iterator_steps_like_sync() {
10401 let entry = function(
10402 0,
10403 5,
10404 vec![
10405 Instruction::CreateArray { dst: reg(0) },
10406 Instruction::LoadConst {
10407 dst: reg(1),
10408 constant: cid(0),
10409 },
10410 Instruction::ArrayPush {
10411 array: reg(0),
10412 value: reg(1),
10413 },
10414 Instruction::GetIterator {
10415 dst: reg(2),
10416 src: reg(0),
10417 kind: IteratorKind::Async,
10418 },
10419 Instruction::IteratorNext {
10420 done: reg(3),
10421 value: reg(4),
10422 iterator: reg(2),
10423 },
10424 Instruction::Return { value: reg(4) },
10425 ],
10426 vec![],
10427 );
10428 let module = verified(vec![Constant::Int32(8)], vec![entry]);
10429 let execution = run_ok(&module);
10430 assert_eq!(execution.value, Value::int32(8));
10431 assert_eq!(execution.entry_registers[3], Value::FALSE);
10432 }
10433
10434 #[test]
10435 fn globals_store_load_and_typeof_undeclared() {
10436 let entry = function(
10439 0,
10440 3,
10441 vec![
10442 Instruction::LoadConst {
10443 dst: reg(0),
10444 constant: cid(2),
10445 },
10446 Instruction::StoreGlobal {
10447 name: cid(0),
10448 value: reg(0),
10449 },
10450 Instruction::TypeOfGlobal {
10451 dst: reg(1),
10452 name: cid(1),
10453 },
10454 Instruction::TypeOfGlobal {
10455 dst: reg(2),
10456 name: cid(0),
10457 },
10458 Instruction::LoadGlobal {
10459 dst: reg(0),
10460 name: cid(0),
10461 },
10462 Instruction::Return { value: reg(0) },
10463 ],
10464 vec![],
10465 );
10466 let module = verified(
10467 vec![
10468 Constant::String(EcmaString::from_utf8("x")),
10469 Constant::String(EcmaString::from_utf8("y")),
10470 Constant::Int32(5),
10471 ],
10472 vec![entry],
10473 );
10474 assert_eq!(run_ok(&module).value, Value::int32(5));
10475 }
10476
10477 #[test]
10478 fn create_cell_throws_reference_error_before_initialization() {
10479 let module = verified(
10480 vec![Constant::Int32(0)],
10481 vec![function(
10482 0,
10483 3,
10484 vec![
10485 Instruction::CreateCell { dst: reg(0) },
10486 Instruction::LoadConst {
10487 dst: reg(1),
10488 constant: cid(0),
10489 },
10490 Instruction::GetProperty {
10491 dst: reg(2),
10492 object: reg(0),
10493 key: reg(1),
10494 },
10495 Instruction::Return { value: reg(2) },
10496 ],
10497 vec![],
10498 )],
10499 );
10500 let mut host = TestHost;
10501 let error = Machine::new(&module, &mut host, Limits::default())
10502 .run()
10503 .expect_err("uninitialized cell read throws");
10504 assert!(matches!(
10505 error.kind,
10506 RuntimeErrorKind::UncaughtThrow {
10507 origin: ThrowOrigin::ReferenceError { .. },
10508 ..
10509 }
10510 ));
10511 }
10512
10513 #[test]
10514 fn create_cell_can_be_initialized_to_undefined() {
10515 let module = verified(
10516 vec![Constant::Int32(0), Constant::Undefined],
10517 vec![function(
10518 0,
10519 4,
10520 vec![
10521 Instruction::CreateCell { dst: reg(0) },
10522 Instruction::LoadConst {
10523 dst: reg(1),
10524 constant: cid(0),
10525 },
10526 Instruction::LoadConst {
10527 dst: reg(2),
10528 constant: cid(1),
10529 },
10530 Instruction::SetProperty {
10531 object: reg(0),
10532 key: reg(1),
10533 value: reg(2),
10534 },
10535 Instruction::GetProperty {
10536 dst: reg(3),
10537 object: reg(0),
10538 key: reg(1),
10539 },
10540 Instruction::Return { value: reg(3) },
10541 ],
10542 vec![],
10543 )],
10544 );
10545 let mut host = TestHost;
10546 let execution = Machine::new(&module, &mut host, Limits::default())
10547 .run()
10548 .expect("explicit undefined initializes the cell");
10549 assert_eq!(execution.value, Value::UNDEFINED);
10550 }
10551
10552 #[test]
10553 fn load_undeclared_global_throws_reference_error() {
10554 let module = verified(
10555 vec![Constant::String(EcmaString::from_utf8("missing"))],
10556 vec![function(
10557 0,
10558 2,
10559 vec![
10560 Instruction::LoadGlobal {
10561 dst: reg(0),
10562 name: cid(0),
10563 },
10564 Instruction::Halt,
10565 Instruction::Return { value: reg(1) },
10566 ],
10567 vec![ExceptionHandler {
10568 start: pc(0),
10569 end: pc(1),
10570 handler: pc(2),
10571 catch_register: reg(1),
10572 }],
10573 )],
10574 );
10575 let mut host = TestHost;
10576 let execution = Machine::new(&module, &mut host, Limits::default())
10579 .run()
10580 .unwrap();
10581 assert_eq!(execution.value, Value::UNDEFINED);
10582 }
10583
10584 #[test]
10585 fn uncaught_reference_error_reports_origin() {
10586 let module = verified(
10587 vec![Constant::String(EcmaString::from_utf8("missing"))],
10588 vec![function(
10589 0,
10590 1,
10591 vec![
10592 Instruction::LoadGlobal {
10593 dst: reg(0),
10594 name: cid(0),
10595 },
10596 Instruction::Return { value: reg(0) },
10597 ],
10598 vec![],
10599 )],
10600 );
10601 let mut host = TestHost;
10602 let error = Machine::new(&module, &mut host, Limits::default())
10603 .run()
10604 .unwrap_err();
10605 assert_eq!(error.pc, pc(0));
10606 assert!(matches!(
10607 error.kind,
10608 RuntimeErrorKind::UncaughtThrow {
10609 origin: ThrowOrigin::ReferenceError { .. },
10610 ..
10611 }
10612 ));
10613 }
10614
10615 fn assert_uri_error(global: &str, argument: EcmaString) {
10616 let module = verified(
10617 vec![
10618 Constant::String(EcmaString::from_utf8(global)),
10619 Constant::String(argument),
10620 Constant::Undefined,
10621 ],
10622 vec![function(
10623 0,
10624 5,
10625 vec![
10626 Instruction::LoadGlobal {
10627 dst: reg(0),
10628 name: cid(0),
10629 },
10630 Instruction::LoadConst {
10631 dst: reg(1),
10632 constant: cid(1),
10633 },
10634 Instruction::LoadConst {
10635 dst: reg(2),
10636 constant: cid(2),
10637 },
10638 Instruction::CreateArray { dst: reg(3) },
10639 Instruction::ArrayPush {
10640 array: reg(3),
10641 value: reg(1),
10642 },
10643 Instruction::Call {
10644 dst: reg(4),
10645 callee: reg(0),
10646 this_value: reg(2),
10647 arguments: reg(3),
10648 },
10649 Instruction::Return { value: reg(4) },
10650 ],
10651 Vec::new(),
10652 )],
10653 );
10654 let mut host = TestHost;
10655 let error = Machine::new(&module, &mut host, Limits::default())
10656 .run()
10657 .unwrap_err();
10658 assert_eq!(error.pc, pc(5));
10659 assert!(matches!(
10660 error.kind,
10661 RuntimeErrorKind::UncaughtThrow {
10662 origin: ThrowOrigin::UriError {
10663 operation: "URI malformed"
10664 },
10665 ..
10666 }
10667 ));
10668 }
10669
10670 #[test]
10671 fn uri_builtins_report_uri_error() {
10672 for (global, argument) in [
10673 ("encodeURIComponent", EcmaString::from_units(&[0xd800])),
10674 ("decodeURIComponent", EcmaString::from_utf8("%")),
10675 ("decodeURIComponent", EcmaString::from_utf8("%GG")),
10676 ("decodeURIComponent", EcmaString::from_utf8("%FF")),
10677 ("decodeURIComponent", EcmaString::from_utf8("%80")),
10678 ("decodeURIComponent", EcmaString::from_utf8("%C0%80")),
10679 ("decodeURIComponent", EcmaString::from_utf8("%E2%82")),
10680 ("decodeURIComponent", EcmaString::from_utf8("%ED%A0%80")),
10681 ("decodeURIComponent", EcmaString::from_utf8("%F4%90%80%80")),
10682 (
10683 "decodeURIComponent",
10684 EcmaString::from_utf8("%F8%80%80%80%80"),
10685 ),
10686 ] {
10687 assert_uri_error(global, argument);
10688 }
10689 }
10690
10691 fn assert_uri_decode(argument: EcmaString, expected: EcmaString) {
10692 let module = verified(
10693 vec![
10694 Constant::String(EcmaString::from_utf8("decodeURIComponent")),
10695 Constant::String(argument),
10696 Constant::Undefined,
10697 Constant::String(expected),
10698 ],
10699 vec![function(
10700 0,
10701 7,
10702 vec![
10703 Instruction::LoadGlobal {
10704 dst: reg(0),
10705 name: cid(0),
10706 },
10707 Instruction::LoadConst {
10708 dst: reg(1),
10709 constant: cid(1),
10710 },
10711 Instruction::LoadConst {
10712 dst: reg(2),
10713 constant: cid(2),
10714 },
10715 Instruction::CreateArray { dst: reg(3) },
10716 Instruction::ArrayPush {
10717 array: reg(3),
10718 value: reg(1),
10719 },
10720 Instruction::Call {
10721 dst: reg(4),
10722 callee: reg(0),
10723 this_value: reg(2),
10724 arguments: reg(3),
10725 },
10726 Instruction::LoadConst {
10727 dst: reg(5),
10728 constant: cid(3),
10729 },
10730 Instruction::Binary {
10731 dst: reg(6),
10732 op: BinaryOp::StrictEqual,
10733 left: reg(4),
10734 right: reg(5),
10735 },
10736 Instruction::Return { value: reg(6) },
10737 ],
10738 Vec::new(),
10739 )],
10740 );
10741 let mut host = TestHost;
10742 let execution = Machine::new(&module, &mut host, Limits::default())
10743 .run()
10744 .unwrap();
10745 assert_eq!(execution.value, Value::TRUE);
10746 }
10747
10748 #[test]
10749 fn decode_uri_component_preserves_units_and_decodes_utf8() {
10750 let exact = EcmaString::from_units(&[0xd800, 0x61, 0xdfff]);
10751 for (argument, expected) in [
10752 (exact.clone(), exact),
10753 (EcmaString::from_utf8("%2F"), EcmaString::from_utf8("/")),
10754 (
10755 EcmaString::from_utf8("%F0%9F%98%80"),
10756 EcmaString::from_utf8("😀"),
10757 ),
10758 (
10759 EcmaString::from_utf8("%E4%B8%ADA"),
10760 EcmaString::from_utf8("ä¸A"),
10761 ),
10762 (EcmaString::from_utf8("%00"), EcmaString::from_units(&[0])),
10763 ] {
10764 assert_uri_decode(argument, expected);
10765 }
10766 }
10767
10768 #[test]
10769 fn regexp_is_object_with_source_and_flags() {
10770 let module = verified(
10772 vec![
10773 Constant::String(EcmaString::from_utf8("ab")),
10774 Constant::String(EcmaString::from_utf8("gi")),
10775 Constant::String(EcmaString::from_utf8("source")),
10776 Constant::String(EcmaString::from_utf8("global")),
10777 ],
10778 vec![function(
10779 0,
10780 4,
10781 vec![
10782 Instruction::CreateRegExp {
10783 dst: reg(0),
10784 pattern: cid(0),
10785 flags: cid(1),
10786 },
10787 Instruction::LoadConst {
10788 dst: reg(1),
10789 constant: cid(3),
10790 },
10791 Instruction::GetProperty {
10792 dst: reg(2),
10793 object: reg(0),
10794 key: reg(1),
10795 },
10796 Instruction::Unary {
10797 dst: reg(3),
10798 op: UnaryOp::TypeOf,
10799 operand: reg(0),
10800 },
10801 Instruction::Return { value: reg(2) },
10802 ],
10803 vec![],
10804 )],
10805 );
10806 let execution = run_ok(&module);
10807 assert_eq!(execution.value, Value::TRUE);
10809 }
10810
10811 #[test]
10812 fn this_and_new_target_are_frame_owned() {
10813 let entry = function(
10815 0,
10816 4,
10817 vec![
10818 Instruction::CreateObject { dst: reg(0) },
10819 Instruction::CreateArray { dst: reg(3) },
10820 Instruction::CreateClosure {
10821 dst: reg(1),
10822 function: FunctionId::new(1),
10823 captures: reg(3),
10824 },
10825 Instruction::CreateArray { dst: reg(2) },
10826 Instruction::Call {
10827 dst: reg(0),
10828 callee: reg(1),
10829 this_value: reg(0),
10830 arguments: reg(2),
10831 },
10832 Instruction::Return { value: reg(0) },
10833 ],
10834 vec![],
10835 );
10836 let callee = function(
10839 0,
10840 2,
10841 vec![
10842 Instruction::LoadNewTarget { dst: reg(0) },
10843 Instruction::Unary {
10844 dst: reg(1),
10845 op: UnaryOp::TypeOf,
10846 operand: reg(0),
10847 },
10848 Instruction::Return { value: reg(1) },
10849 ],
10850 vec![],
10851 );
10852 let module = verified(vec![], vec![entry, callee]);
10853 let execution = run_ok(&module);
10854 assert_ne!(execution.value, Value::UNDEFINED);
10858 }
10859
10860 #[test]
10861 fn new_target_is_constructor_during_construct() {
10862 let entry = function(
10865 0,
10866 4,
10867 vec![
10868 Instruction::CreateArray { dst: reg(3) },
10869 Instruction::CreateClosure {
10870 dst: reg(0),
10871 function: FunctionId::new(1),
10872 captures: reg(3),
10873 },
10874 Instruction::CreateObject { dst: reg(1) },
10876 Instruction::LoadConst {
10877 dst: reg(2),
10878 constant: cid(0),
10879 },
10880 Instruction::SetProperty {
10881 object: reg(0),
10882 key: reg(2),
10883 value: reg(1),
10884 },
10885 Instruction::CreateArray { dst: reg(3) },
10886 Instruction::Construct {
10887 dst: reg(1),
10888 callee: reg(0),
10889 arguments: reg(3),
10890 },
10891 Instruction::LoadConst {
10893 dst: reg(2),
10894 constant: cid(1),
10895 },
10896 Instruction::GetProperty {
10897 dst: reg(3),
10898 object: reg(1),
10899 key: reg(2),
10900 },
10901 Instruction::Binary {
10902 dst: reg(3),
10903 op: BinaryOp::StrictEqual,
10904 left: reg(3),
10905 right: reg(0),
10906 },
10907 Instruction::Return { value: reg(3) },
10908 ],
10909 vec![],
10910 );
10911 let ctor = function(
10912 0,
10913 3,
10914 vec![
10915 Instruction::LoadNewTarget { dst: reg(0) },
10916 Instruction::LoadThis { dst: reg(1) },
10917 Instruction::LoadConst {
10918 dst: reg(2),
10919 constant: cid(1),
10920 },
10921 Instruction::SetProperty {
10922 object: reg(1),
10923 key: reg(2),
10924 value: reg(0),
10925 },
10926 Instruction::Halt,
10927 ],
10928 vec![],
10929 );
10930 let module = verified(
10931 vec![
10932 Constant::String(EcmaString::from_utf8("prototype")),
10933 Constant::String(EcmaString::from_utf8("nt")),
10934 ],
10935 vec![entry, ctor],
10936 );
10937 assert_eq!(run_ok(&module).value, Value::TRUE);
10938 }
10939
10940 #[test]
10941 fn arguments_object_reflects_passed_values() {
10942 let entry = function(
10944 0,
10945 4,
10946 vec![
10947 Instruction::CreateArray { dst: reg(3) },
10948 Instruction::CreateClosure {
10949 dst: reg(0),
10950 function: FunctionId::new(1),
10951 captures: reg(3),
10952 },
10953 Instruction::CreateArray { dst: reg(2) },
10955 Instruction::LoadConst {
10956 dst: reg(1),
10957 constant: cid(0),
10958 },
10959 Instruction::ArrayPush {
10960 array: reg(2),
10961 value: reg(1),
10962 },
10963 Instruction::Call {
10964 dst: reg(0),
10965 callee: reg(0),
10966 this_value: reg(1),
10967 arguments: reg(2),
10968 },
10969 Instruction::Return { value: reg(0) },
10970 ],
10971 vec![],
10972 );
10973 let callee = function(
10974 0,
10975 2,
10976 vec![
10977 Instruction::LoadArguments { dst: reg(0) },
10978 Instruction::LoadConst {
10979 dst: reg(1),
10980 constant: cid(1),
10981 },
10982 Instruction::GetProperty {
10983 dst: reg(0),
10984 object: reg(0),
10985 key: reg(1),
10986 },
10987 Instruction::Return { value: reg(0) },
10988 ],
10989 vec![],
10990 );
10991 let module = verified(
10992 vec![
10993 Constant::Int32(42),
10994 Constant::String(EcmaString::from_utf8("0")),
10995 ],
10996 vec![entry, callee],
10997 );
10998 assert_eq!(run_ok(&module).value, Value::int32(42));
10999 }
11000
11001 #[test]
11002 fn catch_register_receives_exact_thrown_value() {
11003 let module = verified(
11004 vec![Constant::Int32(9)],
11005 vec![function(
11006 0,
11007 2,
11008 vec![
11009 Instruction::LoadConst {
11010 dst: reg(0),
11011 constant: cid(0),
11012 },
11013 Instruction::Throw { value: reg(0) },
11014 Instruction::Return { value: reg(1) },
11015 ],
11016 vec![ExceptionHandler {
11017 start: pc(1),
11018 end: pc(2),
11019 handler: pc(2),
11020 catch_register: reg(1),
11021 }],
11022 )],
11023 );
11024 assert_eq!(run_ok(&module).value, Value::int32(9));
11025 }
11026
11027 #[test]
11028 fn native_callback_throw_is_caught_at_outer_call_site() {
11029 let entry = function(
11030 0,
11031 9,
11032 vec![
11033 Instruction::CreateArray { dst: reg(0) },
11034 Instruction::LoadConst {
11035 dst: reg(1),
11036 constant: cid(0),
11037 },
11038 Instruction::ArrayPush {
11039 array: reg(0),
11040 value: reg(1),
11041 },
11042 Instruction::CreateArray { dst: reg(2) },
11043 Instruction::CreateClosure {
11044 dst: reg(3),
11045 function: FunctionId::new(1),
11046 captures: reg(2),
11047 },
11048 Instruction::LoadConst {
11049 dst: reg(4),
11050 constant: cid(1),
11051 },
11052 Instruction::GetProperty {
11053 dst: reg(5),
11054 object: reg(0),
11055 key: reg(4),
11056 },
11057 Instruction::CreateArray { dst: reg(6) },
11058 Instruction::ArrayPush {
11059 array: reg(6),
11060 value: reg(3),
11061 },
11062 Instruction::Call {
11063 dst: reg(7),
11064 callee: reg(5),
11065 this_value: reg(0),
11066 arguments: reg(6),
11067 },
11068 Instruction::Halt,
11069 Instruction::Return { value: reg(8) },
11070 ],
11071 vec![ExceptionHandler {
11072 start: pc(9),
11073 end: pc(10),
11074 handler: pc(11),
11075 catch_register: reg(8),
11076 }],
11077 );
11078 let callback = closure_function(
11079 0,
11080 0,
11081 1,
11082 vec![
11083 Instruction::LoadConst {
11084 dst: reg(0),
11085 constant: cid(0),
11086 },
11087 Instruction::Throw { value: reg(0) },
11088 ],
11089 );
11090 let module = verified(
11091 vec![
11092 Constant::Int32(7),
11093 Constant::String(EcmaString::from_utf8("map")),
11094 ],
11095 vec![entry, callback],
11096 );
11097
11098 assert_eq!(run_ok(&module).value, Value::int32(7));
11099 }
11100
11101 #[test]
11102 fn native_callback_throw_uncaught_at_outer_call_site() {
11103 let entry = function(
11104 0,
11105 9,
11106 vec![
11107 Instruction::CreateArray { dst: reg(0) },
11108 Instruction::LoadConst {
11109 dst: reg(1),
11110 constant: cid(0),
11111 },
11112 Instruction::ArrayPush {
11113 array: reg(0),
11114 value: reg(1),
11115 },
11116 Instruction::CreateArray { dst: reg(2) },
11117 Instruction::CreateClosure {
11118 dst: reg(3),
11119 function: FunctionId::new(1),
11120 captures: reg(2),
11121 },
11122 Instruction::LoadConst {
11123 dst: reg(4),
11124 constant: cid(1),
11125 },
11126 Instruction::GetProperty {
11127 dst: reg(5),
11128 object: reg(0),
11129 key: reg(4),
11130 },
11131 Instruction::CreateArray { dst: reg(6) },
11132 Instruction::ArrayPush {
11133 array: reg(6),
11134 value: reg(3),
11135 },
11136 Instruction::Call {
11137 dst: reg(7),
11138 callee: reg(5),
11139 this_value: reg(0),
11140 arguments: reg(6),
11141 },
11142 Instruction::Halt,
11143 ],
11144 Vec::new(),
11145 );
11146 let callback = closure_function(
11147 0,
11148 0,
11149 1,
11150 vec![
11151 Instruction::LoadConst {
11152 dst: reg(0),
11153 constant: cid(0),
11154 },
11155 Instruction::Throw { value: reg(0) },
11156 ],
11157 );
11158 let simple = closure_function(
11159 0,
11160 0,
11161 1,
11162 vec![
11163 Instruction::LoadConst {
11164 dst: reg(0),
11165 constant: cid(2),
11166 },
11167 Instruction::Return { value: reg(0) },
11168 ],
11169 );
11170 let module = verified(
11171 vec![
11172 Constant::Int32(7),
11173 Constant::String(EcmaString::from_utf8("map")),
11174 Constant::Int32(42),
11175 ],
11176 vec![entry, callback, simple],
11177 );
11178
11179 let mut host = TestHost;
11180 let mut machine = Machine::new(&module, &mut host, Limits::default());
11181 let error = machine.run_loop(0).unwrap_err();
11182 assert_eq!(
11183 error.kind,
11184 RuntimeErrorKind::UncaughtThrow {
11185 value: Value::int32(7),
11186 origin: ThrowOrigin::Bytecode,
11187 }
11188 );
11189 assert!(machine.callback_boundaries.is_empty());
11190 assert!(machine.frames.is_empty());
11191 assert_eq!(machine.live_registers, 0);
11192
11193 let callee = machine
11194 .allocate(HeapEntry::Function {
11195 module: ModuleId::new(0),
11196 function: FunctionId::new(2),
11197 captures: Vec::new(),
11198 properties: PropertyMap::default(),
11199 prototype: Some(machine.intrinsics.function_prototype),
11200 extensible: true,
11201 })
11202 .unwrap();
11203 assert_eq!(
11204 machine.call_value(callee, Value::UNDEFINED, &[]).unwrap(),
11205 Value::int32(42)
11206 );
11207 }
11208
11209 #[test]
11210 fn callee_throw_unwinds_to_call_site_handler() {
11211 let entry = function(
11212 0,
11213 4,
11214 vec![
11215 Instruction::CreateArray { dst: reg(3) },
11216 Instruction::CreateClosure {
11217 dst: reg(0),
11218 function: FunctionId::new(1),
11219 captures: reg(3),
11220 },
11221 Instruction::CreateArray { dst: reg(1) },
11222 Instruction::Call {
11223 dst: reg(2),
11224 callee: reg(0),
11225 this_value: reg(1),
11226 arguments: reg(1),
11227 },
11228 Instruction::Halt,
11229 Instruction::Return { value: reg(3) },
11230 ],
11231 vec![ExceptionHandler {
11232 start: pc(3),
11233 end: pc(4),
11234 handler: pc(5),
11235 catch_register: reg(3),
11236 }],
11237 );
11238 let callee = function(
11239 0,
11240 1,
11241 vec![
11242 Instruction::LoadConst {
11243 dst: reg(0),
11244 constant: cid(0),
11245 },
11246 Instruction::Throw { value: reg(0) },
11247 ],
11248 vec![],
11249 );
11250 let module = verified(vec![Constant::Int32(7)], vec![entry, callee]);
11251 assert_eq!(run_ok(&module).value, Value::int32(7));
11252 }
11253
11254 #[test]
11255 fn heap_and_register_limits_fail_before_unbounded_growth() {
11256 let module = verified(
11257 vec![],
11258 vec![function(
11259 0,
11260 2,
11261 vec![
11262 Instruction::CreateObject { dst: reg(0) },
11263 Instruction::CreateObject { dst: reg(1) },
11264 Instruction::Halt,
11265 ],
11266 vec![],
11267 )],
11268 );
11269 let mut host = TestHost;
11270 let error = Machine::new(
11271 &module,
11272 &mut host,
11273 Limits {
11274 max_heap_slots: 1,
11275 ..Limits::default()
11276 },
11277 )
11278 .run()
11279 .unwrap_err();
11280 assert_eq!(error.pc, pc(1));
11281 assert_eq!(
11282 error.kind,
11283 RuntimeErrorKind::HeapSlotLimitExceeded { limit: 1 }
11284 );
11285
11286 let mut host = TestHost;
11287 let error = Machine::new(
11288 &module,
11289 &mut host,
11290 Limits {
11291 max_total_registers: 1,
11292 ..Limits::default()
11293 },
11294 )
11295 .run()
11296 .unwrap_err();
11297 assert_eq!(
11298 error.kind,
11299 RuntimeErrorKind::RegisterLimitExceeded { limit: 1 }
11300 );
11301 }
11302
11303 #[test]
11304 fn argument_array_length_limit_is_enforced() {
11305 let entry = function(
11306 0,
11307 4,
11308 vec![
11309 Instruction::CreateArray { dst: reg(3) },
11310 Instruction::CreateClosure {
11311 dst: reg(0),
11312 function: FunctionId::new(1),
11313 captures: reg(3),
11314 },
11315 Instruction::CreateArray { dst: reg(2) },
11316 Instruction::LoadConst {
11317 dst: reg(1),
11318 constant: cid(0),
11319 },
11320 Instruction::ArrayPush {
11321 array: reg(2),
11322 value: reg(1),
11323 },
11324 Instruction::Call {
11325 dst: reg(0),
11326 callee: reg(0),
11327 this_value: reg(1),
11328 arguments: reg(2),
11329 },
11330 Instruction::Halt,
11331 ],
11332 vec![],
11333 );
11334 let callee = function(1, 1, vec![Instruction::Return { value: reg(0) }], vec![]);
11335 let module = verified(vec![Constant::Int32(1)], vec![entry, callee]);
11336 let mut host = TestHost;
11337 let error = Machine::new(
11338 &module,
11339 &mut host,
11340 Limits {
11341 max_argument_count: 0,
11342 ..Limits::default()
11343 },
11344 )
11345 .run()
11346 .unwrap_err();
11347 assert_eq!(
11348 error.kind,
11349 RuntimeErrorKind::ArgumentLimitExceeded {
11350 limit: 0,
11351 requested: 1
11352 }
11353 );
11354 }
11355
11356 #[test]
11357 fn u32_registers_and_instruction_pcs_do_not_truncate_at_127() {
11358 let mut code = vec![Instruction::LoadConst {
11359 dst: reg(0),
11360 constant: cid(0),
11361 }];
11362 for register in 1..=199 {
11363 code.push(Instruction::Move {
11364 dst: reg(register),
11365 src: reg(register - 1),
11366 });
11367 }
11368 code.push(Instruction::Return { value: reg(199) });
11369 let module = verified(
11370 vec![Constant::Number(NumberBits::from_f64(3.5))],
11371 vec![function(0, 200, code, vec![])],
11372 );
11373 let execution = run_ok(&module);
11374 assert_eq!(execution.value, Value::number(3.5));
11375 assert_eq!(execution.entry_registers[199], Value::number(3.5));
11376 }
11377
11378 #[test]
11379 fn construct_returned_object_overrides_default_instance() {
11380 let entry = function(
11382 0,
11383 3,
11384 vec![
11385 Instruction::CreateArray { dst: reg(2) },
11386 Instruction::CreateClosure {
11387 dst: reg(0),
11388 function: FunctionId::new(1),
11389 captures: reg(2),
11390 },
11391 Instruction::CreateArray { dst: reg(2) },
11392 Instruction::Construct {
11393 dst: reg(1),
11394 callee: reg(0),
11395 arguments: reg(2),
11396 },
11397 Instruction::LoadConst {
11399 dst: reg(0),
11400 constant: cid(0),
11401 },
11402 Instruction::GetProperty {
11403 dst: reg(2),
11404 object: reg(1),
11405 key: reg(0),
11406 },
11407 Instruction::Return { value: reg(2) },
11408 ],
11409 vec![],
11410 );
11411 let returns_object = function(
11412 0,
11413 3,
11414 vec![
11415 Instruction::CreateObject { dst: reg(0) },
11416 Instruction::LoadConst {
11417 dst: reg(1),
11418 constant: cid(0),
11419 },
11420 Instruction::LoadConst {
11421 dst: reg(2),
11422 constant: cid(1),
11423 },
11424 Instruction::SetProperty {
11425 object: reg(0),
11426 key: reg(1),
11427 value: reg(2),
11428 },
11429 Instruction::Return { value: reg(0) },
11430 ],
11431 vec![],
11432 );
11433 let module = verified(
11434 vec![
11435 Constant::String(EcmaString::from_utf8("marker")),
11436 Constant::Int32(5),
11437 ],
11438 vec![entry, returns_object],
11439 );
11440 assert_eq!(run_ok(&module).value, Value::int32(5));
11441 }
11442
11443 #[test]
11444 fn ecmascript_number_formatting_is_shortest_round_trip() {
11445 let cases = [
11446 (0.1 + 0.2, "0.30000000000000004"),
11447 (1e21, "1e+21"),
11448 (-0.0, "0"),
11449 (1.0 / 3.0, "0.3333333333333333"),
11450 (1e-6, "0.000001"),
11451 (1e-7, "1e-7"),
11452 ];
11453 for (number, expected) in cases {
11454 assert_eq!(
11455 Machine::<TestHost>::ordinary_number_to_string(number),
11456 expected
11457 );
11458 }
11459 }
11460
11461 #[test]
11462 fn own_keys_put_indices_before_insertion_ordered_strings() {
11463 let module = verified(
11464 Vec::new(),
11465 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11466 );
11467 let mut host = TestHost;
11468 let mut machine = Machine::new(&module, &mut host, Limits::default());
11469 let object = machine
11470 .allocate(HeapEntry::Object {
11471 properties: PropertyMap::default(),
11472 prototype: Some(machine.intrinsics.object_prototype),
11473 boxed_primitive: None,
11474 extensible: true,
11475 })
11476 .unwrap();
11477 let index = machine.runtime_slot(object).unwrap().unwrap();
11478 for (key, value) in [("b", 1), ("2", 2), ("a", 3), ("1", 4)] {
11479 machine
11480 .set_own_data(
11481 index,
11482 PropertyKey::Named(EcmaString::from_utf8(key)),
11483 Value::int32(value),
11484 )
11485 .unwrap();
11486 }
11487 assert_eq!(
11488 machine.enumerable_keys(object).unwrap(),
11489 ["1", "2", "b", "a"].map(EcmaString::from_utf8)
11490 );
11491 }
11492
11493 #[test]
11494 fn object_prototype_to_string_uses_realm_tags() {
11495 let module = verified(
11496 Vec::new(),
11497 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11498 );
11499 let mut host = TestHost;
11500 let mut machine = Machine::new(&module, &mut host, Limits::default());
11501 let array = machine
11502 .allocate(HeapEntry::Array {
11503 elements: Vec::new(),
11504 properties: PropertyMap::default(),
11505 prototype: Some(machine.intrinsics.array_prototype),
11506 extensible: true,
11507 length_writable: true,
11508 })
11509 .unwrap();
11510 let object = machine
11511 .allocate(HeapEntry::Object {
11512 properties: PropertyMap::default(),
11513 prototype: Some(machine.intrinsics.object_prototype),
11514 boxed_primitive: None,
11515 extensible: true,
11516 })
11517 .unwrap();
11518 let function = machine.intrinsics.global("Object").unwrap();
11519 let to_string = machine.intrinsics.object_to_string();
11520 for (value, expected) in [
11521 (Value::UNDEFINED, "[object Undefined]"),
11522 (Value::NULL, "[object Null]"),
11523 (Value::TRUE, "[object Boolean]"),
11524 (array, "[object Array]"),
11525 (object, "[object Object]"),
11526 (function, "[object Function]"),
11527 ] {
11528 let tag = machine.call_value(to_string, value, &[]).unwrap();
11529 assert!(
11530 machine
11531 .string_text(tag)
11532 .is_some_and(|text| text.eq_ascii(expected))
11533 );
11534 }
11535 }
11536
11537 #[derive(Default)]
11538 struct CapabilityHost {
11539 stdout: Vec<u8>,
11540 stderr: Vec<u8>,
11541 env: BTreeMap<String, String>,
11542 }
11543
11544 impl Host for CapabilityHost {
11545 fn write_stdout(&mut self, bytes: &[u8]) {
11546 self.stdout.extend_from_slice(bytes);
11547 }
11548
11549 fn write_stderr(&mut self, bytes: &[u8]) {
11550 self.stderr.extend_from_slice(bytes);
11551 }
11552
11553 fn env(&self, name: &str) -> Option<&str> {
11554 self.env.get(name).map(String::as_str)
11555 }
11556
11557 fn set_env(&mut self, name: &str, value: &str) {
11558 self.env.insert(name.to_owned(), value.to_owned());
11559 }
11560
11561 fn delete_env(&mut self, name: &str) -> bool {
11562 self.env.remove(name).is_some()
11563 }
11564 }
11565
11566 #[test]
11567 fn console_formats_node_value_shapes_byte_exactly() {
11568 let module = verified(
11569 Vec::new(),
11570 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11571 );
11572 let mut host = CapabilityHost::default();
11573 {
11574 let mut machine = Machine::new(&module, &mut host, Limits::default());
11575 let console = machine.intrinsics.global("console").unwrap();
11576 let log = machine.get_named_property(console, "log").unwrap();
11577 let string = machine
11578 .allocate(HeapEntry::String(EcmaString::from_utf8("hello")))
11579 .unwrap();
11580 let array_string = machine
11581 .allocate(HeapEntry::String(EcmaString::from_utf8("x")))
11582 .unwrap();
11583 let array = machine
11584 .allocate(HeapEntry::Array {
11585 elements: vec![Value::int32(1), array_string],
11586 properties: PropertyMap::default(),
11587 prototype: Some(machine.intrinsics.array_prototype),
11588 extensible: true,
11589 length_writable: true,
11590 })
11591 .unwrap();
11592 let mut inner_properties = PropertyMap::default();
11593 inner_properties.insert(
11594 PropertyKey::Named(EcmaString::from_utf8("answer")),
11595 Property::Data {
11596 value: Value::int32(42),
11597 writable: true,
11598 enumerable: true,
11599 configurable: true,
11600 },
11601 );
11602 let inner = machine
11603 .allocate(HeapEntry::Object {
11604 properties: inner_properties,
11605 prototype: Some(machine.intrinsics.object_prototype),
11606 boxed_primitive: None,
11607 extensible: true,
11608 })
11609 .unwrap();
11610 let mut outer_properties = PropertyMap::default();
11611 outer_properties.insert(
11612 PropertyKey::Named(EcmaString::from_utf8("nested")),
11613 Property::Data {
11614 value: inner,
11615 writable: true,
11616 enumerable: true,
11617 configurable: true,
11618 },
11619 );
11620 let outer = machine
11621 .allocate(HeapEntry::Object {
11622 properties: outer_properties,
11623 prototype: Some(machine.intrinsics.object_prototype),
11624 boxed_primitive: None,
11625 extensible: true,
11626 })
11627 .unwrap();
11628 let symbol = machine
11629 .allocate(HeapEntry::Symbol {
11630 description: EcmaString::from_utf8("token"),
11631 })
11632 .unwrap();
11633 for value in [
11634 string,
11635 Value::int32(42),
11636 array,
11637 outer,
11638 Value::UNDEFINED,
11639 Value::NULL,
11640 symbol,
11641 ] {
11642 machine.call_value(log, console, &[value]).unwrap();
11643 }
11644 }
11645 assert_eq!(
11646 host.stdout,
11647 b"hello\n42\n[ 1, 'x' ]\n{ nested: { answer: 42 } }\nundefined\nnull\nSymbol(token)\n"
11648 );
11649 assert!(host.stderr.is_empty());
11650 }
11651
11652 #[test]
11653 fn console_and_process_properties_are_reassignable_and_env_is_live() {
11654 let module = verified(
11655 Vec::new(),
11656 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11657 );
11658 let mut host = CapabilityHost::default();
11659 {
11660 let mut machine = Machine::new(&module, &mut host, Limits::default());
11661 let console = machine.intrinsics.global("console").unwrap();
11662 let warn = machine.get_named_property(console, "warn").unwrap();
11663 machine
11664 .set_data_property(console, "warn", Value::int32(91))
11665 .unwrap();
11666 assert_eq!(
11667 machine.get_named_property(console, "warn").unwrap(),
11668 Value::int32(91)
11669 );
11670 machine.set_data_property(console, "warn", warn).unwrap();
11671
11672 let process = machine.intrinsics.global("process").unwrap();
11673 let env = machine.get_named_property(process, "env").unwrap();
11674 machine
11675 .set_data_property(env, "BAMTS_MODE", Value::int32(7))
11676 .unwrap();
11677 let value = machine.get_named_property(env, "BAMTS_MODE").unwrap();
11678 assert!(
11679 machine
11680 .string_text(value)
11681 .is_some_and(|text| text.eq_ascii("7"))
11682 );
11683 assert!(
11684 machine
11685 .delete_property(
11686 env,
11687 &PropertyKey::Named(EcmaString::from_utf8("BAMTS_MODE"))
11688 )
11689 .unwrap()
11690 );
11691 assert_eq!(
11692 machine.get_named_property(env, "BAMTS_MODE").unwrap(),
11693 Value::UNDEFINED
11694 );
11695 }
11696 assert_eq!(host.env("BAMTS_MODE"), None);
11697 }
11698
11699 #[test]
11700 fn independent_modules_keep_same_name_globals_isolated() {
11701 let dependency = |name: &str, value: i32| {
11702 program_module(
11703 name,
11704 vec![
11705 Constant::String(EcmaString::from_utf8("x")),
11706 Constant::Int32(value),
11707 ],
11708 vec![function(
11709 0,
11710 1,
11711 vec![
11712 Instruction::LoadConst {
11713 dst: reg(0),
11714 constant: cid(2),
11715 },
11716 Instruction::StoreGlobal {
11717 name: cid(1),
11718 value: reg(0),
11719 },
11720 Instruction::Return { value: reg(0) },
11721 ],
11722 Vec::new(),
11723 )],
11724 Vec::new(),
11725 vec![Binding {
11726 name: cid(1),
11727 kind: BindingKind::Hoisted,
11728 }],
11729 vec![Export {
11730 name: cid(1),
11731 source: ExportSource::Local(BindingId::new(0)),
11732 }],
11733 )
11734 };
11735 let root = program_module(
11736 "root",
11737 vec![
11738 Constant::String(EcmaString::from_utf8("left")),
11739 Constant::String(EcmaString::from_utf8("right")),
11740 Constant::String(EcmaString::from_utf8("x")),
11741 ],
11742 vec![function(
11743 0,
11744 5,
11745 vec![
11746 Instruction::LoadGlobal {
11747 dst: reg(0),
11748 name: cid(1),
11749 },
11750 Instruction::LoadGlobal {
11751 dst: reg(1),
11752 name: cid(2),
11753 },
11754 Instruction::LoadConst {
11755 dst: reg(2),
11756 constant: cid(3),
11757 },
11758 Instruction::GetProperty {
11759 dst: reg(3),
11760 object: reg(0),
11761 key: reg(2),
11762 },
11763 Instruction::GetProperty {
11764 dst: reg(4),
11765 object: reg(1),
11766 key: reg(2),
11767 },
11768 Instruction::Binary {
11769 dst: reg(0),
11770 op: BinaryOp::Add,
11771 left: reg(3),
11772 right: reg(4),
11773 },
11774 Instruction::Return { value: reg(0) },
11775 ],
11776 Vec::new(),
11777 )],
11778 vec![
11779 Edge {
11780 specifier: cid(1),
11781 target: EdgeTarget::Local(ModuleId::new(0)),
11782 kind: EdgeKind::Static,
11783 },
11784 Edge {
11785 specifier: cid(2),
11786 target: EdgeTarget::Local(ModuleId::new(1)),
11787 kind: EdgeKind::Static,
11788 },
11789 ],
11790 vec![
11791 Binding {
11792 name: cid(1),
11793 kind: BindingKind::Namespace {
11794 edge: EdgeId::new(0),
11795 },
11796 },
11797 Binding {
11798 name: cid(2),
11799 kind: BindingKind::Namespace {
11800 edge: EdgeId::new(1),
11801 },
11802 },
11803 ],
11804 Vec::new(),
11805 );
11806 let program = linked(vec![dependency("left", 1), dependency("right", 2), root], 2);
11807 assert_eq!(run_ok(&program).value, Value::int32(3));
11808 }
11809
11810 #[test]
11811 fn imported_binding_observes_post_link_mutation_live() {
11812 let dependency = program_module(
11813 "dependency",
11814 vec![
11815 Constant::String(EcmaString::from_utf8("x")),
11816 Constant::Int32(1),
11817 Constant::Int32(2),
11818 Constant::String(EcmaString::from_utf8("set")),
11819 ],
11820 vec![
11821 function(
11822 0,
11823 3,
11824 vec![
11825 Instruction::LoadConst {
11826 dst: reg(0),
11827 constant: cid(2),
11828 },
11829 Instruction::StoreGlobal {
11830 name: cid(1),
11831 value: reg(0),
11832 },
11833 Instruction::CreateArray { dst: reg(1) },
11834 Instruction::CreateClosure {
11835 dst: reg(2),
11836 function: FunctionId::new(1),
11837 captures: reg(1),
11838 },
11839 Instruction::StoreGlobal {
11840 name: cid(4),
11841 value: reg(2),
11842 },
11843 Instruction::Return { value: reg(0) },
11844 ],
11845 Vec::new(),
11846 ),
11847 function(
11848 0,
11849 1,
11850 vec![
11851 Instruction::LoadConst {
11852 dst: reg(0),
11853 constant: cid(3),
11854 },
11855 Instruction::StoreGlobal {
11856 name: cid(1),
11857 value: reg(0),
11858 },
11859 Instruction::Return { value: reg(0) },
11860 ],
11861 Vec::new(),
11862 ),
11863 ],
11864 Vec::new(),
11865 vec![
11866 Binding {
11867 name: cid(1),
11868 kind: BindingKind::Hoisted,
11869 },
11870 Binding {
11871 name: cid(4),
11872 kind: BindingKind::Hoisted,
11873 },
11874 ],
11875 vec![
11876 Export {
11877 name: cid(1),
11878 source: ExportSource::Local(BindingId::new(0)),
11879 },
11880 Export {
11881 name: cid(4),
11882 source: ExportSource::Local(BindingId::new(1)),
11883 },
11884 ],
11885 );
11886 let root = program_module(
11887 "root",
11888 vec![
11889 Constant::String(EcmaString::from_utf8("x")),
11890 Constant::String(EcmaString::from_utf8("set")),
11891 Constant::String(EcmaString::from_utf8("dep")),
11892 ],
11893 vec![function(
11894 0,
11895 3,
11896 vec![
11897 Instruction::LoadGlobal {
11898 dst: reg(0),
11899 name: cid(2),
11900 },
11901 Instruction::CreateArray { dst: reg(1) },
11902 Instruction::Call {
11903 dst: reg(2),
11904 callee: reg(0),
11905 this_value: reg(1),
11906 arguments: reg(1),
11907 },
11908 Instruction::LoadGlobal {
11909 dst: reg(0),
11910 name: cid(1),
11911 },
11912 Instruction::Return { value: reg(0) },
11913 ],
11914 Vec::new(),
11915 )],
11916 vec![Edge {
11917 specifier: cid(3),
11918 target: EdgeTarget::Local(ModuleId::new(0)),
11919 kind: EdgeKind::Static,
11920 }],
11921 vec![
11922 Binding {
11923 name: cid(1),
11924 kind: BindingKind::Imported {
11925 edge: EdgeId::new(0),
11926 name: cid(1),
11927 },
11928 },
11929 Binding {
11930 name: cid(2),
11931 kind: BindingKind::Imported {
11932 edge: EdgeId::new(0),
11933 name: cid(2),
11934 },
11935 },
11936 ],
11937 Vec::new(),
11938 );
11939 assert_eq!(
11940 run_ok(&linked(vec![dependency, root], 1)).value,
11941 Value::int32(2)
11942 );
11943 }
11944
11945 #[test]
11946 fn closure_globals_resolve_in_the_defining_module() {
11947 let dependency = program_module(
11948 "dependency",
11949 vec![
11950 Constant::String(EcmaString::from_utf8("x")),
11951 Constant::Int32(10),
11952 Constant::String(EcmaString::from_utf8("read")),
11953 ],
11954 vec![
11955 function(
11956 0,
11957 3,
11958 vec![
11959 Instruction::LoadConst {
11960 dst: reg(0),
11961 constant: cid(2),
11962 },
11963 Instruction::StoreGlobal {
11964 name: cid(1),
11965 value: reg(0),
11966 },
11967 Instruction::CreateArray { dst: reg(1) },
11968 Instruction::CreateClosure {
11969 dst: reg(2),
11970 function: FunctionId::new(1),
11971 captures: reg(1),
11972 },
11973 Instruction::StoreGlobal {
11974 name: cid(3),
11975 value: reg(2),
11976 },
11977 Instruction::Return { value: reg(0) },
11978 ],
11979 Vec::new(),
11980 ),
11981 function(
11982 0,
11983 1,
11984 vec![
11985 Instruction::LoadGlobal {
11986 dst: reg(0),
11987 name: cid(1),
11988 },
11989 Instruction::Return { value: reg(0) },
11990 ],
11991 Vec::new(),
11992 ),
11993 ],
11994 Vec::new(),
11995 vec![
11996 Binding {
11997 name: cid(1),
11998 kind: BindingKind::Hoisted,
11999 },
12000 Binding {
12001 name: cid(3),
12002 kind: BindingKind::Hoisted,
12003 },
12004 ],
12005 vec![Export {
12006 name: cid(3),
12007 source: ExportSource::Local(BindingId::new(1)),
12008 }],
12009 );
12010 let root = program_module(
12011 "root",
12012 vec![
12013 Constant::String(EcmaString::from_utf8("x")),
12014 Constant::Int32(20),
12015 Constant::String(EcmaString::from_utf8("read")),
12016 Constant::String(EcmaString::from_utf8("dep")),
12017 ],
12018 vec![function(
12019 0,
12020 4,
12021 vec![
12022 Instruction::LoadConst {
12023 dst: reg(0),
12024 constant: cid(2),
12025 },
12026 Instruction::StoreGlobal {
12027 name: cid(1),
12028 value: reg(0),
12029 },
12030 Instruction::LoadGlobal {
12031 dst: reg(1),
12032 name: cid(3),
12033 },
12034 Instruction::CreateArray { dst: reg(2) },
12035 Instruction::Call {
12036 dst: reg(3),
12037 callee: reg(1),
12038 this_value: reg(2),
12039 arguments: reg(2),
12040 },
12041 Instruction::Return { value: reg(3) },
12042 ],
12043 Vec::new(),
12044 )],
12045 vec![Edge {
12046 specifier: cid(4),
12047 target: EdgeTarget::Local(ModuleId::new(0)),
12048 kind: EdgeKind::Static,
12049 }],
12050 vec![
12051 Binding {
12052 name: cid(1),
12053 kind: BindingKind::Hoisted,
12054 },
12055 Binding {
12056 name: cid(3),
12057 kind: BindingKind::Imported {
12058 edge: EdgeId::new(0),
12059 name: cid(3),
12060 },
12061 },
12062 ],
12063 Vec::new(),
12064 );
12065 assert_eq!(
12066 run_ok(&linked(vec![dependency, root], 1)).value,
12067 Value::int32(10)
12068 );
12069 }
12070
12071 #[test]
12072 fn cycle_traps_a_lexical_read_before_initialization() {
12073 let first = program_module(
12074 "first",
12075 vec![
12076 Constant::String(EcmaString::from_utf8("a")),
12077 Constant::Int32(1),
12078 Constant::String(EcmaString::from_utf8("second")),
12079 ],
12080 vec![function(
12081 0,
12082 1,
12083 vec![
12084 Instruction::LoadConst {
12085 dst: reg(0),
12086 constant: cid(2),
12087 },
12088 Instruction::StoreGlobal {
12089 name: cid(1),
12090 value: reg(0),
12091 },
12092 Instruction::Return { value: reg(0) },
12093 ],
12094 Vec::new(),
12095 )],
12096 vec![Edge {
12097 specifier: cid(3),
12098 target: EdgeTarget::Local(ModuleId::new(1)),
12099 kind: EdgeKind::Static,
12100 }],
12101 vec![Binding {
12102 name: cid(1),
12103 kind: BindingKind::Lexical,
12104 }],
12105 vec![Export {
12106 name: cid(1),
12107 source: ExportSource::Local(BindingId::new(0)),
12108 }],
12109 );
12110 let second = program_module(
12111 "second",
12112 vec![
12113 Constant::String(EcmaString::from_utf8("a")),
12114 Constant::String(EcmaString::from_utf8("first")),
12115 ],
12116 vec![function(
12117 0,
12118 1,
12119 vec![
12120 Instruction::LoadGlobal {
12121 dst: reg(0),
12122 name: cid(1),
12123 },
12124 Instruction::Return { value: reg(0) },
12125 ],
12126 Vec::new(),
12127 )],
12128 vec![Edge {
12129 specifier: cid(2),
12130 target: EdgeTarget::Local(ModuleId::new(0)),
12131 kind: EdgeKind::Static,
12132 }],
12133 vec![Binding {
12134 name: cid(1),
12135 kind: BindingKind::Imported {
12136 edge: EdgeId::new(0),
12137 name: cid(1),
12138 },
12139 }],
12140 Vec::new(),
12141 );
12142 let program = linked(vec![first, second], 0);
12143 let mut host = TestHost;
12144 let error = Machine::new(&program, &mut host, Limits::default())
12145 .run()
12146 .unwrap_err();
12147 assert!(matches!(
12148 error.kind,
12149 RuntimeErrorKind::TemporalDeadZone { module, binding }
12150 if module == ModuleId::new(1) && binding == BindingId::new(0)
12151 ));
12152 }
12153
12154 #[test]
12155 fn cycle_reentry_with_a_hoisted_binding_completes() {
12156 let first = program_module(
12157 "first",
12158 vec![
12159 Constant::String(EcmaString::from_utf8("a")),
12160 Constant::Int32(1),
12161 Constant::String(EcmaString::from_utf8("second")),
12162 ],
12163 vec![function(
12164 0,
12165 1,
12166 vec![
12167 Instruction::LoadConst {
12168 dst: reg(0),
12169 constant: cid(2),
12170 },
12171 Instruction::StoreGlobal {
12172 name: cid(1),
12173 value: reg(0),
12174 },
12175 Instruction::Return { value: reg(0) },
12176 ],
12177 Vec::new(),
12178 )],
12179 vec![Edge {
12180 specifier: cid(3),
12181 target: EdgeTarget::Local(ModuleId::new(1)),
12182 kind: EdgeKind::Static,
12183 }],
12184 vec![Binding {
12185 name: cid(1),
12186 kind: BindingKind::Hoisted,
12187 }],
12188 vec![Export {
12189 name: cid(1),
12190 source: ExportSource::Local(BindingId::new(0)),
12191 }],
12192 );
12193 let second = program_module(
12194 "second",
12195 vec![
12196 Constant::String(EcmaString::from_utf8("a")),
12197 Constant::String(EcmaString::from_utf8("first")),
12198 ],
12199 vec![function(
12200 0,
12201 1,
12202 vec![
12203 Instruction::LoadGlobal {
12204 dst: reg(0),
12205 name: cid(1),
12206 },
12207 Instruction::Return { value: reg(0) },
12208 ],
12209 Vec::new(),
12210 )],
12211 vec![Edge {
12212 specifier: cid(2),
12213 target: EdgeTarget::Local(ModuleId::new(0)),
12214 kind: EdgeKind::Static,
12215 }],
12216 vec![Binding {
12217 name: cid(1),
12218 kind: BindingKind::Imported {
12219 edge: EdgeId::new(0),
12220 name: cid(1),
12221 },
12222 }],
12223 Vec::new(),
12224 );
12225 assert_eq!(
12226 run_ok(&linked(vec![first, second], 0)).value,
12227 Value::int32(1)
12228 );
12229 }
12230
12231 #[test]
12232 fn namespace_identity_reads_live_cells_and_enumerates_sorted_keys() {
12233 let dependency = program_module(
12234 "dependency",
12235 vec![
12236 Constant::String(EcmaString::from_utf8("z")),
12237 Constant::String(EcmaString::from_utf8("a")),
12238 Constant::String(EcmaString::from_utf8("mutate")),
12239 Constant::Int32(1),
12240 Constant::Int32(2),
12241 Constant::Int32(3),
12242 ],
12243 vec![
12244 function(
12245 0,
12246 4,
12247 vec![
12248 Instruction::LoadConst {
12249 dst: reg(0),
12250 constant: cid(4),
12251 },
12252 Instruction::StoreGlobal {
12253 name: cid(1),
12254 value: reg(0),
12255 },
12256 Instruction::LoadConst {
12257 dst: reg(0),
12258 constant: cid(5),
12259 },
12260 Instruction::StoreGlobal {
12261 name: cid(2),
12262 value: reg(0),
12263 },
12264 Instruction::CreateArray { dst: reg(1) },
12265 Instruction::CreateClosure {
12266 dst: reg(2),
12267 function: FunctionId::new(1),
12268 captures: reg(1),
12269 },
12270 Instruction::StoreGlobal {
12271 name: cid(3),
12272 value: reg(2),
12273 },
12274 Instruction::Return { value: reg(0) },
12275 ],
12276 Vec::new(),
12277 ),
12278 function(
12279 0,
12280 1,
12281 vec![
12282 Instruction::LoadConst {
12283 dst: reg(0),
12284 constant: cid(6),
12285 },
12286 Instruction::StoreGlobal {
12287 name: cid(1),
12288 value: reg(0),
12289 },
12290 Instruction::Return { value: reg(0) },
12291 ],
12292 Vec::new(),
12293 ),
12294 ],
12295 Vec::new(),
12296 vec![
12297 Binding {
12298 name: cid(1),
12299 kind: BindingKind::Hoisted,
12300 },
12301 Binding {
12302 name: cid(2),
12303 kind: BindingKind::Hoisted,
12304 },
12305 Binding {
12306 name: cid(3),
12307 kind: BindingKind::Hoisted,
12308 },
12309 ],
12310 vec![
12311 Export {
12312 name: cid(1),
12313 source: ExportSource::Local(BindingId::new(0)),
12314 },
12315 Export {
12316 name: cid(2),
12317 source: ExportSource::Local(BindingId::new(1)),
12318 },
12319 Export {
12320 name: cid(3),
12321 source: ExportSource::Local(BindingId::new(2)),
12322 },
12323 ],
12324 );
12325 let root = program_module(
12326 "root",
12327 vec![
12328 Constant::String(EcmaString::from_utf8("ns1")),
12329 Constant::String(EcmaString::from_utf8("ns2")),
12330 Constant::String(EcmaString::from_utf8("mutate")),
12331 Constant::String(EcmaString::from_utf8("z")),
12332 Constant::String(EcmaString::from_utf8("a")),
12333 Constant::String(EcmaString::from_utf8("dep")),
12334 Constant::String(EcmaString::from_utf8("Object")),
12335 Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
12336 Constant::String(EcmaString::from_utf8("value")),
12337 Constant::String(EcmaString::from_utf8("writable")),
12338 Constant::String(EcmaString::from_utf8("enumerable")),
12339 Constant::String(EcmaString::from_utf8("configurable")),
12340 Constant::String(EcmaString::from_utf8("missing")),
12341 ],
12342 vec![function(
12343 0,
12344 31,
12345 vec![
12346 Instruction::LoadGlobal {
12347 dst: reg(0),
12348 name: cid(1),
12349 },
12350 Instruction::LoadGlobal {
12351 dst: reg(1),
12352 name: cid(2),
12353 },
12354 Instruction::Binary {
12355 dst: reg(2),
12356 op: BinaryOp::StrictEqual,
12357 left: reg(0),
12358 right: reg(1),
12359 },
12360 Instruction::LoadGlobal {
12361 dst: reg(3),
12362 name: cid(3),
12363 },
12364 Instruction::CreateArray { dst: reg(4) },
12365 Instruction::Call {
12366 dst: reg(5),
12367 callee: reg(3),
12368 this_value: reg(4),
12369 arguments: reg(4),
12370 },
12371 Instruction::LoadConst {
12372 dst: reg(6),
12373 constant: cid(4),
12374 },
12375 Instruction::GetProperty {
12376 dst: reg(7),
12377 object: reg(0),
12378 key: reg(6),
12379 },
12380 Instruction::GetIterator {
12381 dst: reg(8),
12382 src: reg(0),
12383 kind: IteratorKind::Keys,
12384 },
12385 Instruction::IteratorNext {
12386 done: reg(9),
12387 value: reg(10),
12388 iterator: reg(8),
12389 },
12390 Instruction::LoadConst {
12391 dst: reg(11),
12392 constant: cid(5),
12393 },
12394 Instruction::Binary {
12395 dst: reg(12),
12396 op: BinaryOp::StrictEqual,
12397 left: reg(10),
12398 right: reg(11),
12399 },
12400 Instruction::IteratorNext {
12401 done: reg(9),
12402 value: reg(10),
12403 iterator: reg(8),
12404 },
12405 Instruction::LoadConst {
12406 dst: reg(13),
12407 constant: cid(3),
12408 },
12409 Instruction::Binary {
12410 dst: reg(5),
12411 op: BinaryOp::StrictEqual,
12412 left: reg(10),
12413 right: reg(13),
12414 },
12415 Instruction::IteratorNext {
12416 done: reg(9),
12417 value: reg(10),
12418 iterator: reg(8),
12419 },
12420 Instruction::Binary {
12421 dst: reg(14),
12422 op: BinaryOp::StrictEqual,
12423 left: reg(10),
12424 right: reg(6),
12425 },
12426 Instruction::LoadGlobal {
12427 dst: reg(15),
12428 name: cid(7),
12429 },
12430 Instruction::LoadConst {
12431 dst: reg(16),
12432 constant: cid(8),
12433 },
12434 Instruction::GetProperty {
12435 dst: reg(17),
12436 object: reg(15),
12437 key: reg(16),
12438 },
12439 Instruction::CreateArray { dst: reg(18) },
12440 Instruction::ArrayPush {
12441 array: reg(18),
12442 value: reg(0),
12443 },
12444 Instruction::ArrayPush {
12445 array: reg(18),
12446 value: reg(6),
12447 },
12448 Instruction::Call {
12449 dst: reg(19),
12450 callee: reg(17),
12451 this_value: reg(18),
12452 arguments: reg(18),
12453 },
12454 Instruction::LoadConst {
12455 dst: reg(20),
12456 constant: cid(9),
12457 },
12458 Instruction::GetProperty {
12459 dst: reg(21),
12460 object: reg(19),
12461 key: reg(20),
12462 },
12463 Instruction::LoadConst {
12464 dst: reg(22),
12465 constant: cid(10),
12466 },
12467 Instruction::GetProperty {
12468 dst: reg(23),
12469 object: reg(19),
12470 key: reg(22),
12471 },
12472 Instruction::LoadConst {
12473 dst: reg(24),
12474 constant: cid(11),
12475 },
12476 Instruction::GetProperty {
12477 dst: reg(25),
12478 object: reg(19),
12479 key: reg(24),
12480 },
12481 Instruction::LoadConst {
12482 dst: reg(26),
12483 constant: cid(12),
12484 },
12485 Instruction::GetProperty {
12486 dst: reg(27),
12487 object: reg(19),
12488 key: reg(26),
12489 },
12490 Instruction::CreateArray { dst: reg(28) },
12491 Instruction::LoadConst {
12492 dst: reg(29),
12493 constant: cid(13),
12494 },
12495 Instruction::ArrayPush {
12496 array: reg(28),
12497 value: reg(0),
12498 },
12499 Instruction::ArrayPush {
12500 array: reg(28),
12501 value: reg(29),
12502 },
12503 Instruction::Call {
12504 dst: reg(30),
12505 callee: reg(17),
12506 this_value: reg(28),
12507 arguments: reg(28),
12508 },
12509 Instruction::Return { value: reg(21) },
12510 ],
12511 Vec::new(),
12512 )],
12513 vec![Edge {
12514 specifier: cid(6),
12515 target: EdgeTarget::Local(ModuleId::new(0)),
12516 kind: EdgeKind::Static,
12517 }],
12518 vec![
12519 Binding {
12520 name: cid(1),
12521 kind: BindingKind::Namespace {
12522 edge: EdgeId::new(0),
12523 },
12524 },
12525 Binding {
12526 name: cid(2),
12527 kind: BindingKind::Namespace {
12528 edge: EdgeId::new(0),
12529 },
12530 },
12531 Binding {
12532 name: cid(3),
12533 kind: BindingKind::Imported {
12534 edge: EdgeId::new(0),
12535 name: cid(3),
12536 },
12537 },
12538 ],
12539 Vec::new(),
12540 );
12541 let execution = run_ok(&linked(vec![dependency, root], 1));
12542 assert_eq!(execution.value, Value::int32(3));
12543 assert_eq!(execution.entry_registers[2], Value::TRUE);
12544 assert_eq!(execution.entry_registers[5], Value::TRUE);
12545 assert_eq!(execution.entry_registers[12], Value::TRUE);
12546 assert_eq!(execution.entry_registers[14], Value::TRUE);
12547 assert_eq!(execution.entry_registers[23], Value::TRUE);
12548 assert_eq!(execution.entry_registers[25], Value::TRUE);
12549 assert_eq!(execution.entry_registers[27], Value::FALSE);
12550 assert_eq!(execution.entry_registers[30], Value::UNDEFINED);
12551 }
12552
12553 #[test]
12554 fn side_effect_module_runs_once_with_single_or_duplicate_static_edges() {
12555 for duplicate in [false, true] {
12556 let dependency = program_module(
12557 "dependency",
12558 vec![
12559 Constant::String(EcmaString::from_utf8("count")),
12560 Constant::Int32(0),
12561 Constant::Int32(1),
12562 ],
12563 vec![function(
12564 0,
12565 2,
12566 vec![
12567 Instruction::LoadGlobal {
12568 dst: reg(0),
12569 name: cid(1),
12570 },
12571 Instruction::JumpIfFalse {
12572 condition: reg(0),
12573 target: pc(3),
12574 },
12575 Instruction::Jump { target: pc(5) },
12576 Instruction::LoadConst {
12577 dst: reg(0),
12578 constant: cid(2),
12579 },
12580 Instruction::StoreGlobal {
12581 name: cid(1),
12582 value: reg(0),
12583 },
12584 Instruction::LoadConst {
12585 dst: reg(1),
12586 constant: cid(3),
12587 },
12588 Instruction::Binary {
12589 dst: reg(0),
12590 op: BinaryOp::Add,
12591 left: reg(0),
12592 right: reg(1),
12593 },
12594 Instruction::StoreGlobal {
12595 name: cid(1),
12596 value: reg(0),
12597 },
12598 Instruction::Return { value: reg(0) },
12599 ],
12600 Vec::new(),
12601 )],
12602 Vec::new(),
12603 vec![Binding {
12604 name: cid(1),
12605 kind: BindingKind::Hoisted,
12606 }],
12607 vec![Export {
12608 name: cid(1),
12609 source: ExportSource::Local(BindingId::new(0)),
12610 }],
12611 );
12612 let mut edges = vec![Edge {
12613 specifier: cid(2),
12614 target: EdgeTarget::Local(ModuleId::new(0)),
12615 kind: EdgeKind::Static,
12616 }];
12617 if duplicate {
12618 edges.push(Edge {
12619 specifier: cid(3),
12620 target: EdgeTarget::Local(ModuleId::new(0)),
12621 kind: EdgeKind::Static,
12622 });
12623 }
12624 let root = program_module(
12625 "root",
12626 vec![
12627 Constant::String(EcmaString::from_utf8("count")),
12628 Constant::String(EcmaString::from_utf8("dep-one")),
12629 Constant::String(EcmaString::from_utf8("dep-two")),
12630 ],
12631 vec![function(
12632 0,
12633 1,
12634 vec![
12635 Instruction::LoadGlobal {
12636 dst: reg(0),
12637 name: cid(1),
12638 },
12639 Instruction::Return { value: reg(0) },
12640 ],
12641 Vec::new(),
12642 )],
12643 edges,
12644 vec![Binding {
12645 name: cid(1),
12646 kind: BindingKind::Imported {
12647 edge: EdgeId::new(0),
12648 name: cid(1),
12649 },
12650 }],
12651 Vec::new(),
12652 );
12653 assert_eq!(
12654 run_ok(&linked(vec![dependency, root], 1)).value,
12655 Value::int32(1)
12656 );
12657 }
12658 }
12659
12660 #[test]
12661 fn failed_module_rethrows_the_identical_stored_value() {
12662 let module = program_module(
12663 "throws",
12664 Vec::new(),
12665 vec![function(
12666 0,
12667 1,
12668 vec![
12669 Instruction::CreateObject { dst: reg(0) },
12670 Instruction::Throw { value: reg(0) },
12671 ],
12672 Vec::new(),
12673 )],
12674 Vec::new(),
12675 Vec::new(),
12676 Vec::new(),
12677 );
12678 let program = linked(vec![module], 0);
12679 let mut host = TestHost;
12680 let mut machine = Machine::new(&program, &mut host, Limits::default());
12681 machine.frames.clear();
12682 machine.live_registers = 0;
12683 machine.instantiate_modules().unwrap();
12684 let first = machine.evaluate_module(ModuleId::new(0)).unwrap_err();
12685 let second = machine.evaluate_module(ModuleId::new(0)).unwrap_err();
12686 let RuntimeErrorKind::UncaughtThrow { value: first, .. } = first.kind else {
12687 panic!("module must fail by throwing");
12688 };
12689 let RuntimeErrorKind::UncaughtThrow { value: second, .. } = second.kind else {
12690 panic!("stored failure must remain a throw");
12691 };
12692 assert_eq!(first, second);
12693 assert!(first.as_heap_ref().is_some());
12694 }
12695
12696 #[test]
12697 fn external_static_edge_is_a_typed_runtime_error() {
12698 let module = program_module(
12699 "root",
12700 vec![Constant::String(EcmaString::from_utf8("external"))],
12701 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
12702 vec![Edge {
12703 specifier: cid(1),
12704 target: EdgeTarget::External,
12705 kind: EdgeKind::Static,
12706 }],
12707 Vec::new(),
12708 Vec::new(),
12709 );
12710 let program = linked(vec![module], 0);
12711 let mut host = TestHost;
12712 let error = Machine::new(&program, &mut host, Limits::default())
12713 .run()
12714 .unwrap_err();
12715 assert!(matches!(
12716 error.kind,
12717 RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
12718 if module == ModuleId::new(0) && edge == EdgeId::new(0)
12719 ));
12720 }
12721
12722 #[test]
12723 fn external_module_and_export_names_preserve_unicode() {
12724 for (specifier, export) in [("módulo", "value"), ("external", "café")] {
12725 let module = program_module(
12726 "root",
12727 vec![
12728 Constant::String(EcmaString::from_utf8(export)),
12729 Constant::String(EcmaString::from_utf8(specifier)),
12730 ],
12731 vec![function(
12732 0,
12733 1,
12734 vec![
12735 Instruction::LoadGlobal {
12736 dst: reg(0),
12737 name: cid(1),
12738 },
12739 Instruction::Return { value: reg(0) },
12740 ],
12741 Vec::new(),
12742 )],
12743 vec![Edge {
12744 specifier: cid(2),
12745 target: EdgeTarget::External,
12746 kind: EdgeKind::Static,
12747 }],
12748 vec![Binding {
12749 name: cid(1),
12750 kind: BindingKind::Imported {
12751 edge: EdgeId::new(0),
12752 name: cid(1),
12753 },
12754 }],
12755 Vec::new(),
12756 );
12757 let program = linked(vec![module], 0);
12758 let mut host = TestHost;
12759 let mut machine = Machine::new(&program, &mut host, Limits::default());
12760 machine.registry.external.insert(
12761 EcmaString::from_utf8(specifier),
12762 ExternalModuleInstance {
12763 namespace: Value::UNDEFINED,
12764 exports: BTreeMap::from([(
12765 EcmaString::from_utf8(export),
12766 ExternalExport {
12767 value: Value::int32(7),
12768 cell: None,
12769 },
12770 )]),
12771 internals: BTreeMap::new(),
12772 },
12773 );
12774
12775 assert_eq!(machine.run().unwrap().value, Value::int32(7));
12776 }
12777 }
12778
12779 #[test]
12780 fn dynamic_import_preserves_cycles_identity_and_single_evaluation() {
12781 let root = program_module(
12782 "root",
12783 vec![
12784 Constant::String(EcmaString::from_utf8("./dependency")),
12785 Constant::String(EcmaString::from_utf8("count")),
12786 Constant::Int32(0),
12787 Constant::String(EcmaString::from_utf8("value")),
12788 ],
12789 vec![function(
12790 0,
12791 7,
12792 vec![
12793 Instruction::LoadConst {
12794 dst: reg(0),
12795 constant: cid(3),
12796 },
12797 Instruction::StoreGlobal {
12798 name: cid(2),
12799 value: reg(0),
12800 },
12801 Instruction::Import {
12802 dst: reg(1),
12803 specifier: cid(1),
12804 },
12805 Instruction::Import {
12806 dst: reg(2),
12807 specifier: cid(1),
12808 },
12809 Instruction::Binary {
12810 dst: reg(3),
12811 op: BinaryOp::StrictEqual,
12812 left: reg(1),
12813 right: reg(2),
12814 },
12815 Instruction::LoadConst {
12816 dst: reg(4),
12817 constant: cid(4),
12818 },
12819 Instruction::GetProperty {
12820 dst: reg(5),
12821 object: reg(2),
12822 key: reg(4),
12823 },
12824 Instruction::LoadGlobal {
12825 dst: reg(6),
12826 name: cid(2),
12827 },
12828 Instruction::Return { value: reg(5) },
12829 ],
12830 Vec::new(),
12831 )],
12832 vec![Edge {
12833 specifier: cid(1),
12834 target: EdgeTarget::Local(ModuleId::new(1)),
12835 kind: EdgeKind::Dynamic,
12836 }],
12837 Vec::new(),
12838 Vec::new(),
12839 );
12840 let dependency = program_module(
12841 "dependency",
12842 vec![
12843 Constant::String(EcmaString::from_utf8("./root")),
12844 Constant::String(EcmaString::from_utf8("count")),
12845 Constant::Int32(1),
12846 Constant::Int32(7),
12847 Constant::String(EcmaString::from_utf8("value")),
12848 ],
12849 vec![function(
12850 0,
12851 3,
12852 vec![
12853 Instruction::LoadGlobal {
12854 dst: reg(0),
12855 name: cid(2),
12856 },
12857 Instruction::LoadConst {
12858 dst: reg(1),
12859 constant: cid(3),
12860 },
12861 Instruction::Binary {
12862 dst: reg(2),
12863 op: BinaryOp::Add,
12864 left: reg(0),
12865 right: reg(1),
12866 },
12867 Instruction::StoreGlobal {
12868 name: cid(2),
12869 value: reg(2),
12870 },
12871 Instruction::LoadConst {
12872 dst: reg(0),
12873 constant: cid(4),
12874 },
12875 Instruction::StoreGlobal {
12876 name: cid(5),
12877 value: reg(0),
12878 },
12879 Instruction::Return { value: reg(0) },
12880 ],
12881 Vec::new(),
12882 )],
12883 vec![Edge {
12884 specifier: cid(1),
12885 target: EdgeTarget::Local(ModuleId::new(0)),
12886 kind: EdgeKind::Static,
12887 }],
12888 vec![Binding {
12889 name: cid(5),
12890 kind: BindingKind::Hoisted,
12891 }],
12892 vec![Export {
12893 name: cid(5),
12894 source: ExportSource::Local(BindingId::new(0)),
12895 }],
12896 );
12897
12898 let execution = run_ok(&linked(vec![root, dependency], 0));
12899 assert_eq!(execution.value, Value::int32(7));
12900 assert_eq!(execution.entry_registers[1], execution.entry_registers[2]);
12901 assert_eq!(execution.entry_registers[3], Value::TRUE);
12902 assert_eq!(execution.entry_registers[6], Value::int32(1));
12903 }
12904
12905 #[test]
12906 fn dynamic_import_counts_live_registers_and_retries_engine_failures() {
12907 let target = program_module(
12908 "target",
12909 Vec::new(),
12910 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
12911 Vec::new(),
12912 Vec::new(),
12913 Vec::new(),
12914 );
12915 let root = program_module(
12916 "root",
12917 vec![Constant::String(EcmaString::from_utf8("./target"))],
12918 vec![function(
12919 0,
12920 1,
12921 vec![
12922 Instruction::Import {
12923 dst: reg(0),
12924 specifier: cid(1),
12925 },
12926 Instruction::Return { value: reg(0) },
12927 ],
12928 Vec::new(),
12929 )],
12930 vec![Edge {
12931 specifier: cid(1),
12932 target: EdgeTarget::Local(ModuleId::new(1)),
12933 kind: EdgeKind::Dynamic,
12934 }],
12935 Vec::new(),
12936 Vec::new(),
12937 );
12938 let program = linked(vec![root, target], 0);
12939 let mut host = TestHost;
12940 let mut machine = Machine::new(
12941 &program,
12942 &mut host,
12943 Limits {
12944 max_total_registers: 1,
12945 ..Limits::default()
12946 },
12947 );
12948 machine.frames.clear();
12949 machine.live_registers = 0;
12950 machine.instantiate_modules().unwrap();
12951
12952 let error = machine.evaluate_import(ModuleId::new(0)).unwrap_err();
12953 assert!(matches!(
12954 error.kind,
12955 RuntimeErrorKind::RegisterLimitExceeded { limit: 1 }
12956 ));
12957 assert_eq!(machine.frames.len(), 0);
12958 assert_eq!(machine.live_registers, 0);
12959
12960 machine.limits.max_total_registers = 2;
12961 machine.evaluate_import(ModuleId::new(0)).unwrap();
12962 }
12963
12964 #[test]
12965 fn dynamic_import_rethrows_one_stored_failure_at_each_import_site() {
12966 let root = program_module(
12967 "root",
12968 vec![
12969 Constant::String(EcmaString::from_utf8("./target")),
12970 Constant::String(EcmaString::from_utf8("count")),
12971 Constant::Int32(0),
12972 ],
12973 vec![function(
12974 0,
12975 4,
12976 vec![
12977 Instruction::LoadConst {
12978 dst: reg(0),
12979 constant: cid(3),
12980 },
12981 Instruction::StoreGlobal {
12982 name: cid(2),
12983 value: reg(0),
12984 },
12985 Instruction::Import {
12986 dst: reg(0),
12987 specifier: cid(1),
12988 },
12989 Instruction::Halt,
12990 Instruction::Import {
12991 dst: reg(0),
12992 specifier: cid(1),
12993 },
12994 Instruction::Halt,
12995 Instruction::LoadGlobal {
12996 dst: reg(3),
12997 name: cid(2),
12998 },
12999 Instruction::Return { value: reg(2) },
13000 ],
13001 vec![
13002 ExceptionHandler {
13003 start: pc(2),
13004 end: pc(3),
13005 handler: pc(4),
13006 catch_register: reg(1),
13007 },
13008 ExceptionHandler {
13009 start: pc(4),
13010 end: pc(5),
13011 handler: pc(6),
13012 catch_register: reg(2),
13013 },
13014 ],
13015 )],
13016 vec![Edge {
13017 specifier: cid(1),
13018 target: EdgeTarget::Local(ModuleId::new(1)),
13019 kind: EdgeKind::Dynamic,
13020 }],
13021 Vec::new(),
13022 Vec::new(),
13023 );
13024 let target = program_module(
13025 "target",
13026 vec![
13027 Constant::String(EcmaString::from_utf8("count")),
13028 Constant::Int32(1),
13029 Constant::Int32(9),
13030 ],
13031 vec![function(
13032 0,
13033 3,
13034 vec![
13035 Instruction::LoadGlobal {
13036 dst: reg(0),
13037 name: cid(1),
13038 },
13039 Instruction::LoadConst {
13040 dst: reg(1),
13041 constant: cid(2),
13042 },
13043 Instruction::Binary {
13044 dst: reg(2),
13045 op: BinaryOp::Add,
13046 left: reg(0),
13047 right: reg(1),
13048 },
13049 Instruction::StoreGlobal {
13050 name: cid(1),
13051 value: reg(2),
13052 },
13053 Instruction::LoadConst {
13054 dst: reg(0),
13055 constant: cid(3),
13056 },
13057 Instruction::Throw { value: reg(0) },
13058 ],
13059 Vec::new(),
13060 )],
13061 Vec::new(),
13062 Vec::new(),
13063 Vec::new(),
13064 );
13065
13066 let execution = run_ok(&linked(vec![root, target], 0));
13067 assert_eq!(execution.value, Value::int32(9));
13068 assert_eq!(execution.entry_registers[1], Value::int32(9));
13069 assert_eq!(execution.entry_registers[2], Value::int32(9));
13070 assert_eq!(execution.entry_registers[3], Value::int32(1));
13071 }
13072
13073 #[test]
13074 fn dynamic_import_returns_the_registered_external_namespace() {
13075 let module = program_module(
13076 "root",
13077 vec![Constant::String(EcmaString::from_utf8("external"))],
13078 vec![function(
13079 0,
13080 3,
13081 vec![
13082 Instruction::Import {
13083 dst: reg(0),
13084 specifier: cid(1),
13085 },
13086 Instruction::Import {
13087 dst: reg(1),
13088 specifier: cid(1),
13089 },
13090 Instruction::Binary {
13091 dst: reg(2),
13092 op: BinaryOp::StrictEqual,
13093 left: reg(0),
13094 right: reg(1),
13095 },
13096 Instruction::Return { value: reg(2) },
13097 ],
13098 Vec::new(),
13099 )],
13100 vec![Edge {
13101 specifier: cid(1),
13102 target: EdgeTarget::External,
13103 kind: EdgeKind::Dynamic,
13104 }],
13105 Vec::new(),
13106 Vec::new(),
13107 );
13108 let program = linked(vec![module], 0);
13109 let mut host = TestHost;
13110 let mut machine = Machine::new(&program, &mut host, Limits::default());
13111 let namespace = machine
13112 .allocate(HeapEntry::Object {
13113 properties: PropertyMap::default(),
13114 prototype: Some(machine.intrinsics.object_prototype),
13115 boxed_primitive: None,
13116 extensible: true,
13117 })
13118 .unwrap();
13119 machine.registry.external.insert(
13120 EcmaString::from_utf8("external"),
13121 ExternalModuleInstance {
13122 namespace,
13123 exports: BTreeMap::new(),
13124 internals: BTreeMap::new(),
13125 },
13126 );
13127
13128 let execution = machine.run().unwrap();
13129 assert_eq!(execution.value, Value::TRUE);
13130 assert_eq!(execution.entry_registers[0], namespace);
13131 assert_eq!(execution.entry_registers[1], namespace);
13132 }
13133
13134 #[test]
13135 fn dynamic_import_resolution_is_requester_scoped() {
13136 let requester = |name, target| {
13137 program_module(
13138 name,
13139 vec![Constant::String(EcmaString::from_utf8("./target"))],
13140 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13141 vec![Edge {
13142 specifier: cid(1),
13143 target: EdgeTarget::Local(ModuleId::new(target)),
13144 kind: EdgeKind::Dynamic,
13145 }],
13146 Vec::new(),
13147 Vec::new(),
13148 )
13149 };
13150 let target = |name| {
13151 program_module(
13152 name,
13153 Vec::new(),
13154 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13155 Vec::new(),
13156 Vec::new(),
13157 Vec::new(),
13158 )
13159 };
13160 let program = linked(
13161 vec![
13162 requester("first", 2),
13163 requester("second", 3),
13164 target("first-target"),
13165 target("second-target"),
13166 ],
13167 0,
13168 );
13169 let mut host = TestHost;
13170 let machine = Machine::new(&program, &mut host, Limits::default());
13171
13172 assert_eq!(
13173 machine.resolve_import(ModuleId::new(0), cid(1)),
13174 Ok(ImportTarget::Local(ModuleId::new(2)))
13175 );
13176 assert_eq!(
13177 machine.resolve_import(ModuleId::new(1), cid(1)),
13178 Ok(ImportTarget::Local(ModuleId::new(3)))
13179 );
13180 }
13181
13182 #[test]
13183 fn dynamic_import_of_a_missing_external_is_a_runtime_error() {
13184 let module = program_module(
13185 "root",
13186 vec![Constant::String(EcmaString::from_utf8("dynamic"))],
13187 vec![function(
13188 0,
13189 1,
13190 vec![
13191 Instruction::Import {
13192 dst: reg(0),
13193 specifier: cid(1),
13194 },
13195 Instruction::Return { value: reg(0) },
13196 ],
13197 Vec::new(),
13198 )],
13199 vec![Edge {
13200 specifier: cid(1),
13201 target: EdgeTarget::External,
13202 kind: EdgeKind::Dynamic,
13203 }],
13204 Vec::new(),
13205 Vec::new(),
13206 );
13207 let program = linked(vec![module], 0);
13208 let mut host = TestHost;
13209 let error = Machine::new(&program, &mut host, Limits::default())
13210 .run()
13211 .unwrap_err();
13212 assert!(matches!(
13213 error.kind,
13214 RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
13215 if module == ModuleId::new(0) && edge == EdgeId::new(0)
13216 ));
13217 }
13218
13219 #[test]
13220 fn unbound_global_names_fall_back_to_the_realm_global_map() {
13221 let program = verified(
13222 vec![
13223 Constant::String(EcmaString::from_utf8("realmOnly")),
13224 Constant::Int32(7),
13225 ],
13226 vec![function(
13227 0,
13228 1,
13229 vec![
13230 Instruction::LoadConst {
13231 dst: reg(0),
13232 constant: cid(1),
13233 },
13234 Instruction::StoreGlobal {
13235 name: cid(0),
13236 value: reg(0),
13237 },
13238 Instruction::LoadGlobal {
13239 dst: reg(0),
13240 name: cid(0),
13241 },
13242 Instruction::Return { value: reg(0) },
13243 ],
13244 Vec::new(),
13245 )],
13246 );
13247 assert_eq!(run_ok(&program).value, Value::int32(7));
13248 }
13249
13250 #[test]
13251 fn module_cell_limit_is_enforced_before_evaluation() {
13252 let module = program_module(
13253 "root",
13254 vec![Constant::String(EcmaString::from_utf8("x"))],
13255 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13256 Vec::new(),
13257 vec![Binding {
13258 name: cid(1),
13259 kind: BindingKind::Hoisted,
13260 }],
13261 Vec::new(),
13262 );
13263 let program = linked(vec![module], 0);
13264 let mut host = TestHost;
13265 let error = Machine::new(
13266 &program,
13267 &mut host,
13268 Limits {
13269 max_module_cells: 0,
13270 ..Limits::default()
13271 },
13272 )
13273 .run()
13274 .unwrap_err();
13275 assert!(matches!(
13276 error.kind,
13277 RuntimeErrorKind::ModuleCellLimitExceeded { limit: 0 }
13278 ));
13279 }
13280 #[test]
13281 fn imported_binding_store_throws_without_mutating_the_exporter() {
13282 let dependency = program_module(
13283 "dependency",
13284 vec![
13285 Constant::String(EcmaString::from_utf8("x")),
13286 Constant::Int32(1),
13287 ],
13288 vec![function(
13289 0,
13290 1,
13291 vec![
13292 Instruction::LoadConst {
13293 dst: reg(0),
13294 constant: cid(2),
13295 },
13296 Instruction::StoreGlobal {
13297 name: cid(1),
13298 value: reg(0),
13299 },
13300 Instruction::Return { value: reg(0) },
13301 ],
13302 Vec::new(),
13303 )],
13304 Vec::new(),
13305 vec![Binding {
13306 name: cid(1),
13307 kind: BindingKind::Hoisted,
13308 }],
13309 vec![Export {
13310 name: cid(1),
13311 source: ExportSource::Local(BindingId::new(0)),
13312 }],
13313 );
13314 let root = program_module(
13315 "root",
13316 vec![
13317 Constant::String(EcmaString::from_utf8("x")),
13318 Constant::Int32(2),
13319 Constant::String(EcmaString::from_utf8("dep")),
13320 ],
13321 vec![function(
13322 0,
13323 1,
13324 vec![
13325 Instruction::LoadConst {
13326 dst: reg(0),
13327 constant: cid(2),
13328 },
13329 Instruction::StoreGlobal {
13330 name: cid(1),
13331 value: reg(0),
13332 },
13333 Instruction::Return { value: reg(0) },
13334 ],
13335 Vec::new(),
13336 )],
13337 vec![Edge {
13338 specifier: cid(3),
13339 target: EdgeTarget::Local(ModuleId::new(0)),
13340 kind: EdgeKind::Static,
13341 }],
13342 vec![Binding {
13343 name: cid(1),
13344 kind: BindingKind::Imported {
13345 edge: EdgeId::new(0),
13346 name: cid(1),
13347 },
13348 }],
13349 Vec::new(),
13350 );
13351 let program = linked(vec![dependency, root], 1);
13352 let mut host = TestHost;
13353 let mut machine = Machine::new(&program, &mut host, Limits::default());
13354 machine.frames.clear();
13355 machine.live_registers = 0;
13356 machine.instantiate_modules().unwrap();
13357 assert!(machine.evaluate_module(ModuleId::new(1)).is_err());
13358 let exporter = machine.registry.modules[0].binding_cells[0].unwrap();
13359 assert_eq!(machine.registry.cells[exporter.0].value, Value::int32(1));
13360 }
13361
13362 #[test]
13363 fn namespace_descriptor_propagates_temporal_dead_zone() {
13364 let root = program_module(
13365 "root",
13366 vec![
13367 Constant::String(EcmaString::from_utf8("x")),
13368 Constant::Int32(1),
13369 Constant::String(EcmaString::from_utf8("dependency")),
13370 ],
13371 vec![function(
13372 0,
13373 1,
13374 vec![
13375 Instruction::LoadConst {
13376 dst: reg(0),
13377 constant: cid(2),
13378 },
13379 Instruction::StoreGlobal {
13380 name: cid(1),
13381 value: reg(0),
13382 },
13383 Instruction::Return { value: reg(0) },
13384 ],
13385 Vec::new(),
13386 )],
13387 vec![Edge {
13388 specifier: cid(3),
13389 target: EdgeTarget::Local(ModuleId::new(1)),
13390 kind: EdgeKind::Static,
13391 }],
13392 vec![Binding {
13393 name: cid(1),
13394 kind: BindingKind::Lexical,
13395 }],
13396 vec![Export {
13397 name: cid(1),
13398 source: ExportSource::Local(BindingId::new(0)),
13399 }],
13400 );
13401 let dependency = program_module(
13402 "dependency",
13403 vec![
13404 Constant::String(EcmaString::from_utf8("ns")),
13405 Constant::String(EcmaString::from_utf8("root")),
13406 Constant::String(EcmaString::from_utf8("Object")),
13407 Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
13408 Constant::String(EcmaString::from_utf8("x")),
13409 ],
13410 vec![namespace_descriptor_entry()],
13411 vec![Edge {
13412 specifier: cid(2),
13413 target: EdgeTarget::Local(ModuleId::new(0)),
13414 kind: EdgeKind::Static,
13415 }],
13416 vec![Binding {
13417 name: cid(1),
13418 kind: BindingKind::Namespace {
13419 edge: EdgeId::new(0),
13420 },
13421 }],
13422 Vec::new(),
13423 );
13424 let program = linked(vec![root, dependency], 0);
13425 let mut host = TestHost;
13426 let error = Machine::new(&program, &mut host, Limits::default())
13427 .run()
13428 .expect_err("descriptor reads uninitialized namespace export");
13429 assert!(matches!(
13430 error.kind,
13431 RuntimeErrorKind::TemporalDeadZone { module, binding }
13432 if module == ModuleId::new(0) && binding == BindingId::new(0)
13433 ));
13434 }
13435
13436 #[test]
13437 fn namespace_descriptor_propagates_external_linkage_error() {
13438 let exported = program_module(
13439 "exported",
13440 vec![
13441 Constant::String(EcmaString::from_utf8("x")),
13442 Constant::String(EcmaString::from_utf8("external")),
13443 ],
13444 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13445 vec![Edge {
13446 specifier: cid(2),
13447 target: EdgeTarget::External,
13448 kind: EdgeKind::Dynamic,
13449 }],
13450 Vec::new(),
13451 vec![Export {
13452 name: cid(1),
13453 source: ExportSource::Indirect {
13454 edge: EdgeId::new(0),
13455 name: cid(1),
13456 },
13457 }],
13458 );
13459 let importer = program_module(
13460 "importer",
13461 vec![
13462 Constant::String(EcmaString::from_utf8("ns")),
13463 Constant::String(EcmaString::from_utf8("exported")),
13464 Constant::String(EcmaString::from_utf8("Object")),
13465 Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
13466 Constant::String(EcmaString::from_utf8("x")),
13467 ],
13468 vec![namespace_descriptor_entry()],
13469 vec![Edge {
13470 specifier: cid(2),
13471 target: EdgeTarget::Local(ModuleId::new(0)),
13472 kind: EdgeKind::Static,
13473 }],
13474 vec![Binding {
13475 name: cid(1),
13476 kind: BindingKind::Namespace {
13477 edge: EdgeId::new(0),
13478 },
13479 }],
13480 Vec::new(),
13481 );
13482 let program = linked(vec![exported, importer], 1);
13483 let mut host = TestHost;
13484 let error = Machine::new(&program, &mut host, Limits::default())
13485 .run()
13486 .expect_err("descriptor resolves external namespace export");
13487 assert!(matches!(
13488 error.kind,
13489 RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
13490 if module == ModuleId::new(0) && edge == EdgeId::new(0)
13491 ));
13492 }
13493
13494 #[test]
13495 fn installed_script_uses_machine_wide_id_and_keeps_its_code() {
13496 let root = verified(
13497 Vec::new(),
13498 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13499 );
13500 let script = Arc::new(verified(
13501 vec![Constant::Int32(42)],
13502 vec![function(
13503 0,
13504 1,
13505 vec![
13506 Instruction::LoadConst {
13507 dst: reg(0),
13508 constant: cid(0),
13509 },
13510 Instruction::Return { value: reg(0) },
13511 ],
13512 Vec::new(),
13513 )],
13514 ));
13515 let mut host = TestHost;
13516 let mut machine = Machine::new(&root, &mut host, Limits::default());
13517 machine.instantiate_modules().unwrap();
13518 let module = machine.install_script_reserving(script, 0, 0).unwrap();
13519
13520 assert_eq!(module, ModuleId::new(root.modules().len() as u32));
13521 assert!(machine.program().module(module).is_none());
13522 assert_eq!(
13523 machine.module_code(module).constants()[0],
13524 Constant::Int32(42)
13525 );
13526
13527 let closure = machine
13528 .allocate(HeapEntry::Function {
13529 module,
13530 function: FunctionId::new(0),
13531 captures: Vec::new(),
13532 properties: PropertyMap::default(),
13533 prototype: Some(machine.intrinsics.function_prototype),
13534 extensible: true,
13535 })
13536 .unwrap();
13537 assert!(matches!(
13538 machine.call_value(closure, Value::UNDEFINED, &[]),
13539 Ok(value) if value == Value::int32(42)
13540 ));
13541 }
13542
13543 #[test]
13544 fn installed_script_rejects_non_classic_programs_and_enforces_limit() {
13545 let root = verified(
13546 Vec::new(),
13547 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13548 );
13549 let two_modules = Arc::new(linked(
13550 vec![
13551 program_module(
13552 "first",
13553 Vec::new(),
13554 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13555 Vec::new(),
13556 Vec::new(),
13557 Vec::new(),
13558 ),
13559 program_module(
13560 "second",
13561 Vec::new(),
13562 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13563 Vec::new(),
13564 Vec::new(),
13565 Vec::new(),
13566 ),
13567 ],
13568 0,
13569 ));
13570 let script = Arc::new(verified(
13571 Vec::new(),
13572 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13573 ));
13574 let mut host = TestHost;
13575 let mut machine = Machine::new(
13576 &root,
13577 &mut host,
13578 Limits {
13579 max_dynamic_modules: 1,
13580 ..Limits::default()
13581 },
13582 );
13583 machine.instantiate_modules().unwrap();
13584
13585 assert!(matches!(
13586 machine.install_script_reserving(two_modules, 0, 0),
13587 Err(RuntimeErrorKind::InvalidDynamicScript { .. })
13588 ));
13589 machine
13590 .install_script_reserving(script.clone(), 0, 0)
13591 .unwrap();
13592 assert!(matches!(
13593 machine.install_script_reserving(script, 0, 0),
13594 Err(RuntimeErrorKind::DynamicModuleLimitExceeded { limit: 1 })
13595 ));
13596 }
13597
13598 #[test]
13599 fn script_heap_cost_counts_scalar_constant_slots() {
13600 let entry = || vec![function(0, 1, vec![Instruction::Halt], Vec::new())];
13601 let empty = verified(Vec::new(), entry());
13602 let constants = vec![Constant::Int32(0); 128];
13603 let scalars = verified(constants.clone(), entry());
13604
13605 let added = Machine::<TestHost>::script_heap_cost(&scalars)
13606 - Machine::<TestHost>::script_heap_cost(&empty);
13607
13608 assert!(added >= constants.len() * std::mem::size_of::<Constant>());
13609 }
13610
13611 #[test]
13612 fn script_heap_cost_includes_verification_storage() {
13613 let small = verified(
13614 Vec::new(),
13615 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13616 );
13617 let large = verified(
13618 Vec::new(),
13619 vec![function(0, 130, vec![Instruction::Halt], Vec::new())],
13620 );
13621 let small_verification = small.modules()[0].code.verification_bytes();
13622 let large_verification = large.modules()[0].code.verification_bytes();
13623
13624 assert_eq!(
13625 Machine::<TestHost>::script_heap_cost(&large)
13626 - Machine::<TestHost>::script_heap_cost(&small),
13627 large_verification - small_verification
13628 );
13629 }
13630 #[test]
13631 fn promise_resolver_settles_once_and_reactions_wait_for_drain() {
13632 let program = verified(
13633 vec![
13634 Constant::String(EcmaString::from_utf8("resolve")),
13635 Constant::String(EcmaString::from_utf8("reject")),
13636 Constant::String(EcmaString::from_utf8("observed")),
13637 ],
13638 vec![
13639 function(0, 1, vec![Instruction::Halt], Vec::new()),
13640 function(
13641 2,
13642 2,
13643 vec![
13644 Instruction::StoreGlobal {
13645 name: cid(0),
13646 value: reg(0),
13647 },
13648 Instruction::StoreGlobal {
13649 name: cid(1),
13650 value: reg(1),
13651 },
13652 Instruction::Return { value: reg(0) },
13653 ],
13654 Vec::new(),
13655 ),
13656 function(
13657 1,
13658 1,
13659 vec![
13660 Instruction::StoreGlobal {
13661 name: cid(2),
13662 value: reg(0),
13663 },
13664 Instruction::Return { value: reg(0) },
13665 ],
13666 Vec::new(),
13667 ),
13668 ],
13669 );
13670 let mut host = TestHost;
13671 let mut machine = Machine::new(&program, &mut host, Limits::default());
13672 machine.frames.clear();
13673 machine.live_registers = 0;
13674 let executor = machine
13675 .allocate(HeapEntry::Function {
13676 module: ModuleId::new(0),
13677 function: FunctionId::new(1),
13678 captures: Vec::new(),
13679 properties: PropertyMap::default(),
13680 prototype: Some(machine.intrinsics.function_prototype),
13681 extensible: true,
13682 })
13683 .unwrap();
13684 let observer = machine
13685 .allocate(HeapEntry::Function {
13686 module: ModuleId::new(0),
13687 function: FunctionId::new(2),
13688 captures: Vec::new(),
13689 properties: PropertyMap::default(),
13690 prototype: Some(machine.intrinsics.function_prototype),
13691 extensible: true,
13692 })
13693 .unwrap();
13694 let constructor = machine.intrinsics.global("Promise").unwrap();
13695 let constructor_index = machine.runtime_slot(constructor).unwrap().unwrap();
13696 let HeapEntry::NativeFunction {
13697 callable: NativeCallable::Builtin(constructor_id),
13698 ..
13699 } = machine.heap[constructor_index]
13700 else {
13701 panic!("Promise must be a native constructor");
13702 };
13703 let BuiltinOutcome::Value(promise) = machine
13704 .call_builtin(constructor_id, Value::UNDEFINED, &[executor], true)
13705 .unwrap()
13706 else {
13707 panic!("Promise construction returns a Promise");
13708 };
13709 let then = machine.get_named_property(promise, "then").unwrap();
13710 machine
13711 .call_value(then, promise, &[observer])
13712 .expect("then returns a derived Promise");
13713 let resolve = machine
13714 .globals
13715 .get(&EcmaString::from_utf8("resolve"))
13716 .copied()
13717 .unwrap();
13718 let reject = machine
13719 .globals
13720 .get(&EcmaString::from_utf8("reject"))
13721 .copied()
13722 .unwrap();
13723 assert_eq!(
13724 machine
13725 .call_value(resolve, Value::UNDEFINED, &[Value::int32(1)])
13726 .unwrap(),
13727 Value::UNDEFINED
13728 );
13729 assert_eq!(
13730 machine
13731 .call_value(reject, Value::UNDEFINED, &[Value::int32(2)])
13732 .unwrap(),
13733 Value::UNDEFINED
13734 );
13735 assert!(
13736 !machine
13737 .globals
13738 .contains_key(&EcmaString::from_utf8("observed"))
13739 );
13740
13741 let drain = machine.drain_microtasks().unwrap();
13742 assert_eq!(drain.executed, 1);
13743 assert!(drain.uncaught.is_empty());
13744 assert_eq!(
13745 machine
13746 .globals
13747 .get(&EcmaString::from_utf8("observed"))
13748 .copied(),
13749 Some(Value::int32(1))
13750 );
13751 }
13752
13753 #[test]
13754 fn promise_resolution_adopts_thenables_with_a_fresh_resolver() {
13755 let program = verified(
13756 vec![
13757 Constant::String(EcmaString::from_utf8("resolve")),
13758 Constant::String(EcmaString::from_utf8("reject")),
13759 Constant::String(EcmaString::from_utf8("observed")),
13760 Constant::Int32(7),
13761 Constant::Int32(8),
13762 Constant::Int32(9),
13763 Constant::Undefined,
13764 ],
13765 vec![
13766 function(0, 1, vec![Instruction::Halt], Vec::new()),
13767 function(
13768 2,
13769 2,
13770 vec![
13771 Instruction::StoreGlobal {
13772 name: cid(0),
13773 value: reg(0),
13774 },
13775 Instruction::StoreGlobal {
13776 name: cid(1),
13777 value: reg(1),
13778 },
13779 Instruction::Return { value: reg(0) },
13780 ],
13781 Vec::new(),
13782 ),
13783 function(
13784 1,
13785 1,
13786 vec![
13787 Instruction::StoreGlobal {
13788 name: cid(2),
13789 value: reg(0),
13790 },
13791 Instruction::Return { value: reg(0) },
13792 ],
13793 Vec::new(),
13794 ),
13795 function(
13796 2,
13797 6,
13798 vec![
13799 Instruction::LoadConst {
13800 dst: reg(2),
13801 constant: cid(3),
13802 },
13803 Instruction::CreateArray { dst: reg(3) },
13804 Instruction::ArrayPush {
13805 array: reg(3),
13806 value: reg(2),
13807 },
13808 Instruction::LoadConst {
13809 dst: reg(4),
13810 constant: cid(6),
13811 },
13812 Instruction::Call {
13813 dst: reg(5),
13814 callee: reg(0),
13815 this_value: reg(4),
13816 arguments: reg(3),
13817 },
13818 Instruction::LoadConst {
13819 dst: reg(2),
13820 constant: cid(4),
13821 },
13822 Instruction::CreateArray { dst: reg(3) },
13823 Instruction::ArrayPush {
13824 array: reg(3),
13825 value: reg(2),
13826 },
13827 Instruction::Call {
13828 dst: reg(5),
13829 callee: reg(1),
13830 this_value: reg(4),
13831 arguments: reg(3),
13832 },
13833 Instruction::LoadConst {
13834 dst: reg(2),
13835 constant: cid(5),
13836 },
13837 Instruction::Throw { value: reg(2) },
13838 ],
13839 Vec::new(),
13840 ),
13841 ],
13842 );
13843 let mut host = TestHost;
13844 let mut machine = Machine::new(&program, &mut host, Limits::default());
13845 machine.frames.clear();
13846 machine.live_registers = 0;
13847 let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
13848 machine
13849 .allocate(HeapEntry::Function {
13850 module: ModuleId::new(0),
13851 function: FunctionId::new(function),
13852 captures: Vec::new(),
13853 properties: PropertyMap::default(),
13854 prototype: Some(machine.intrinsics.function_prototype),
13855 extensible: true,
13856 })
13857 .unwrap()
13858 };
13859 let executor = runtime_function(&mut machine, 1);
13860 let observer = runtime_function(&mut machine, 2);
13861 let then_callback = runtime_function(&mut machine, 3);
13862 let thenable = machine
13863 .allocate(HeapEntry::Object {
13864 properties: PropertyMap::default(),
13865 prototype: Some(machine.intrinsics.object_prototype),
13866 boxed_primitive: None,
13867 extensible: true,
13868 })
13869 .unwrap();
13870 machine
13871 .set_data_property(thenable, "then", then_callback)
13872 .unwrap();
13873
13874 let constructor = machine.intrinsics.global("Promise").unwrap();
13875 let constructor_index = machine.runtime_slot(constructor).unwrap().unwrap();
13876 let HeapEntry::NativeFunction {
13877 callable: NativeCallable::Builtin(constructor_id),
13878 ..
13879 } = machine.heap[constructor_index]
13880 else {
13881 panic!("Promise must be a native constructor");
13882 };
13883 let BuiltinOutcome::Value(promise) = machine
13884 .call_builtin(constructor_id, Value::UNDEFINED, &[executor], true)
13885 .unwrap()
13886 else {
13887 panic!("Promise construction returns a Promise");
13888 };
13889 let resolve = machine
13890 .globals
13891 .get(&EcmaString::from_utf8("resolve"))
13892 .copied()
13893 .unwrap();
13894 let reject = machine
13895 .globals
13896 .get(&EcmaString::from_utf8("reject"))
13897 .copied()
13898 .unwrap();
13899 machine
13900 .call_value(resolve, Value::UNDEFINED, &[thenable])
13901 .unwrap();
13902 let then = machine.get_named_property(promise, "then").unwrap();
13903 machine.call_value(then, promise, &[observer]).unwrap();
13904 machine
13905 .call_value(reject, Value::UNDEFINED, &[Value::int32(9)])
13906 .unwrap();
13907 assert!(
13908 !machine
13909 .globals
13910 .contains_key(&EcmaString::from_utf8("observed"))
13911 );
13912
13913 let drain = machine.drain_microtasks().unwrap();
13914 assert_eq!(drain.executed, 2);
13915 assert!(drain.uncaught.is_empty());
13916 assert_eq!(
13917 machine
13918 .globals
13919 .get(&EcmaString::from_utf8("observed"))
13920 .copied(),
13921 Some(Value::int32(7))
13922 );
13923 }
13924
13925 #[test]
13926 fn queue_microtask_drains_fifo_including_jobs_added_during_drain() {
13927 let program = verified(
13928 vec![
13929 Constant::String(EcmaString::from_utf8("order")),
13930 Constant::String(EcmaString::from_utf8("queueMicrotask")),
13931 Constant::String(EcmaString::from_utf8("third")),
13932 Constant::Int32(1),
13933 Constant::Int32(2),
13934 Constant::Int32(3),
13935 Constant::Undefined,
13936 ],
13937 vec![
13938 function(0, 1, vec![Instruction::Halt], Vec::new()),
13939 function(
13940 0,
13941 7,
13942 vec![
13943 Instruction::LoadGlobal {
13944 dst: reg(0),
13945 name: cid(0),
13946 },
13947 Instruction::LoadConst {
13948 dst: reg(1),
13949 constant: cid(3),
13950 },
13951 Instruction::ArrayPush {
13952 array: reg(0),
13953 value: reg(1),
13954 },
13955 Instruction::LoadGlobal {
13956 dst: reg(2),
13957 name: cid(1),
13958 },
13959 Instruction::LoadGlobal {
13960 dst: reg(3),
13961 name: cid(2),
13962 },
13963 Instruction::CreateArray { dst: reg(4) },
13964 Instruction::ArrayPush {
13965 array: reg(4),
13966 value: reg(3),
13967 },
13968 Instruction::LoadConst {
13969 dst: reg(5),
13970 constant: cid(6),
13971 },
13972 Instruction::Call {
13973 dst: reg(6),
13974 callee: reg(2),
13975 this_value: reg(5),
13976 arguments: reg(4),
13977 },
13978 Instruction::Return { value: reg(1) },
13979 ],
13980 Vec::new(),
13981 ),
13982 function(
13983 0,
13984 2,
13985 vec![
13986 Instruction::LoadGlobal {
13987 dst: reg(0),
13988 name: cid(0),
13989 },
13990 Instruction::LoadConst {
13991 dst: reg(1),
13992 constant: cid(4),
13993 },
13994 Instruction::ArrayPush {
13995 array: reg(0),
13996 value: reg(1),
13997 },
13998 Instruction::Return { value: reg(1) },
13999 ],
14000 Vec::new(),
14001 ),
14002 function(
14003 0,
14004 2,
14005 vec![
14006 Instruction::LoadGlobal {
14007 dst: reg(0),
14008 name: cid(0),
14009 },
14010 Instruction::LoadConst {
14011 dst: reg(1),
14012 constant: cid(5),
14013 },
14014 Instruction::ArrayPush {
14015 array: reg(0),
14016 value: reg(1),
14017 },
14018 Instruction::Return { value: reg(1) },
14019 ],
14020 Vec::new(),
14021 ),
14022 ],
14023 );
14024 let mut host = TestHost;
14025 let mut machine = Machine::new(&program, &mut host, Limits::default());
14026 machine.frames.clear();
14027 machine.live_registers = 0;
14028 let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
14029 machine
14030 .allocate(HeapEntry::Function {
14031 module: ModuleId::new(0),
14032 function: FunctionId::new(function),
14033 captures: Vec::new(),
14034 properties: PropertyMap::default(),
14035 prototype: Some(machine.intrinsics.function_prototype),
14036 extensible: true,
14037 })
14038 .unwrap()
14039 };
14040 let first = runtime_function(&mut machine, 1);
14041 let second = runtime_function(&mut machine, 2);
14042 let third = runtime_function(&mut machine, 3);
14043 let order = machine
14044 .allocate(HeapEntry::Array {
14045 elements: Vec::new(),
14046 properties: PropertyMap::default(),
14047 prototype: Some(machine.intrinsics.array_prototype),
14048 extensible: true,
14049 length_writable: true,
14050 })
14051 .unwrap();
14052 machine
14053 .globals
14054 .insert(EcmaString::from_utf8("order"), order);
14055 machine
14056 .globals
14057 .insert(EcmaString::from_utf8("third"), third);
14058 let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14059 machine
14060 .call_value(queue, Value::UNDEFINED, &[first])
14061 .unwrap();
14062 machine
14063 .call_value(queue, Value::UNDEFINED, &[second])
14064 .unwrap();
14065
14066 let drain = machine.drain_microtasks().unwrap();
14067 assert_eq!(drain.executed, 3);
14068 assert!(drain.uncaught.is_empty());
14069 let index = machine.runtime_slot(order).unwrap().unwrap();
14070 let HeapEntry::Array { elements, .. } = &machine.heap[index] else {
14071 panic!("order remains an array");
14072 };
14073 assert_eq!(
14074 elements,
14075 &[Value::int32(1), Value::int32(2), Value::int32(3)]
14076 );
14077 }
14078
14079 #[test]
14080 fn queue_microtask_reports_callback_throws_and_continues() {
14081 let program = verified(
14082 vec![
14083 Constant::Int32(7),
14084 Constant::Int32(1),
14085 Constant::String(EcmaString::from_utf8("observed")),
14086 ],
14087 vec![
14088 function(0, 1, vec![Instruction::Halt], Vec::new()),
14089 function(
14090 0,
14091 1,
14092 vec![
14093 Instruction::LoadConst {
14094 dst: reg(0),
14095 constant: cid(0),
14096 },
14097 Instruction::Throw { value: reg(0) },
14098 ],
14099 Vec::new(),
14100 ),
14101 function(
14102 0,
14103 1,
14104 vec![
14105 Instruction::LoadConst {
14106 dst: reg(0),
14107 constant: cid(1),
14108 },
14109 Instruction::StoreGlobal {
14110 name: cid(2),
14111 value: reg(0),
14112 },
14113 Instruction::Return { value: reg(0) },
14114 ],
14115 Vec::new(),
14116 ),
14117 ],
14118 );
14119 let mut host = TestHost;
14120 let mut machine = Machine::new(&program, &mut host, Limits::default());
14121 machine.frames.clear();
14122 machine.live_registers = 0;
14123 let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
14124 machine
14125 .allocate(HeapEntry::Function {
14126 module: ModuleId::new(0),
14127 function: FunctionId::new(function),
14128 captures: Vec::new(),
14129 properties: PropertyMap::default(),
14130 prototype: Some(machine.intrinsics.function_prototype),
14131 extensible: true,
14132 })
14133 .unwrap()
14134 };
14135 let throwing = runtime_function(&mut machine, 1);
14136 let observer = runtime_function(&mut machine, 2);
14137 let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14138 machine
14139 .call_value(queue, Value::UNDEFINED, &[throwing])
14140 .unwrap();
14141 machine
14142 .call_value(queue, Value::UNDEFINED, &[observer])
14143 .unwrap();
14144
14145 let drain = machine.drain_microtasks().unwrap();
14146 assert_eq!(drain.executed, 2);
14147 assert_eq!(
14148 drain.uncaught,
14149 vec![CallbackException {
14150 value: Value::int32(7),
14151 origin: ThrowOrigin::Bytecode,
14152 }]
14153 );
14154 assert_eq!(
14155 machine
14156 .globals
14157 .get(&EcmaString::from_utf8("observed"))
14158 .copied(),
14159 Some(Value::int32(1))
14160 );
14161 }
14162
14163 #[test]
14164 fn microtask_boundaries_preserve_the_queued_head() {
14165 let program = verified(
14166 vec![Constant::Undefined],
14167 vec![
14168 function(0, 1, vec![Instruction::Halt], Vec::new()),
14169 function(
14170 0,
14171 1,
14172 vec![
14173 Instruction::LoadConst {
14174 dst: reg(0),
14175 constant: cid(0),
14176 },
14177 Instruction::Return { value: reg(0) },
14178 ],
14179 Vec::new(),
14180 ),
14181 ],
14182 );
14183 let mut host = TestHost;
14184 let mut machine = Machine::new(
14185 &program,
14186 &mut host,
14187 Limits {
14188 max_microtasks: 1,
14189 ..Limits::default()
14190 },
14191 );
14192 machine.frames.clear();
14193 machine.live_registers = 0;
14194 let callback = machine
14195 .allocate(HeapEntry::Function {
14196 module: ModuleId::new(0),
14197 function: FunctionId::new(1),
14198 captures: Vec::new(),
14199 properties: PropertyMap::default(),
14200 prototype: Some(machine.intrinsics.function_prototype),
14201 extensible: true,
14202 })
14203 .unwrap();
14204 let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14205 assert!(matches!(
14206 machine.call_value(queue, Value::UNDEFINED, &[Value::int32(1)]),
14207 Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
14208 ));
14209 machine
14210 .call_value(queue, Value::UNDEFINED, &[callback])
14211 .unwrap();
14212 assert!(matches!(
14213 machine.call_value(queue, Value::UNDEFINED, &[callback]),
14214 Err(EvalFailure::Runtime(
14215 RuntimeErrorKind::MicrotaskQueueLimitExceeded { limit: 1 }
14216 ))
14217 ));
14218
14219 let fuel = machine.fuel;
14220 machine.microtask_drain_active = true;
14221 let reentry = machine.drain_microtasks().unwrap_err();
14222 assert!(matches!(
14223 reentry.kind,
14224 RuntimeErrorKind::MicrotaskDrainReentry
14225 ));
14226 assert_eq!(machine.fuel, fuel);
14227 assert_eq!(machine.microtasks.len(), 1);
14228 machine.microtask_drain_active = false;
14229
14230 machine.fuel = 0;
14231 let exhausted = machine.drain_microtasks().unwrap_err();
14232 assert!(matches!(
14233 exhausted.kind,
14234 RuntimeErrorKind::FuelExhausted { .. }
14235 ));
14236 assert!(!machine.microtask_drain_active);
14237 assert_eq!(machine.microtasks.len(), 1);
14238
14239 machine.fuel = 100;
14240 let drain = machine.drain_microtasks().unwrap();
14241 assert_eq!(drain.executed, 1);
14242 assert!(machine.microtasks.is_empty());
14243 }
14244
14245 #[derive(Default)]
14248 struct ManualTimerState {
14249 live: std::collections::BTreeMap<u64, u64>,
14250 reports: std::collections::VecDeque<TimerWakeup>,
14251 scheduled: Vec<(u64, u32)>,
14252 cancelled: Vec<u64>,
14253 fail_schedule: bool,
14254 fail_poll: bool,
14255 }
14256
14257 #[derive(Clone, Default)]
14258 struct ManualTimerProvider {
14259 state: std::rc::Rc<std::cell::RefCell<ManualTimerState>>,
14260 }
14261
14262 impl TimerProvider for ManualTimerProvider {
14263 fn schedule(&mut self, id: u64, delay_ms: u32) -> Result<u64, TimerError> {
14264 let mut state = self.state.borrow_mut();
14265 state.scheduled.push((id, delay_ms));
14266 if state.fail_schedule {
14267 return Err(TimerError::new("manual schedule failure"));
14268 }
14269 let deadline = u64::from(delay_ms);
14270 state.live.insert(id, deadline);
14271 Ok(deadline)
14272 }
14273
14274 fn cancel(&mut self, id: u64) -> Result<bool, TimerError> {
14275 let mut state = self.state.borrow_mut();
14276 state.cancelled.push(id);
14277 Ok(state.live.remove(&id).is_some())
14278 }
14279
14280 fn poll_expired(&mut self, output: &mut Vec<TimerWakeup>) -> Result<(), TimerError> {
14281 let mut state = self.state.borrow_mut();
14282 if state.fail_poll {
14283 return Err(TimerError::new("manual poll failure"));
14284 }
14285 output.extend(state.reports.drain(..));
14286 Ok(())
14287 }
14288
14289 fn wait_expired(&mut self) -> Result<Option<TimerWakeup>, TimerError> {
14290 Ok(self.state.borrow_mut().reports.pop_front())
14291 }
14292
14293 fn has_pending(&self) -> bool {
14294 !self.state.borrow().live.is_empty()
14295 }
14296 }
14297
14298 #[derive(Default)]
14299 struct TimerTestHost {
14300 provider: ManualTimerProvider,
14301 }
14302
14303 impl Host for TimerTestHost {
14304 fn timers(&mut self) -> Option<&mut (dyn TimerProvider + 'static)> {
14305 Some(&mut self.provider)
14306 }
14307 }
14308
14309 fn timer_program() -> Program<Verified> {
14310 verified(
14311 vec![
14312 Constant::String(EcmaString::from_utf8("a")),
14313 Constant::String(EcmaString::from_utf8("b")),
14314 Constant::String(EcmaString::from_utf8("this_seen")),
14315 Constant::String(EcmaString::from_utf8("arg_seen")),
14316 Constant::Int32(1),
14317 Constant::Int32(7),
14318 ],
14319 vec![
14320 function(0, 1, vec![Instruction::Halt], Vec::new()),
14321 function(
14322 0,
14323 1,
14324 vec![
14325 Instruction::LoadConst {
14326 dst: reg(0),
14327 constant: cid(4),
14328 },
14329 Instruction::StoreGlobal {
14330 name: cid(0),
14331 value: reg(0),
14332 },
14333 Instruction::Return { value: reg(0) },
14334 ],
14335 Vec::new(),
14336 ),
14337 function(
14338 0,
14339 1,
14340 vec![
14341 Instruction::LoadConst {
14342 dst: reg(0),
14343 constant: cid(4),
14344 },
14345 Instruction::StoreGlobal {
14346 name: cid(1),
14347 value: reg(0),
14348 },
14349 Instruction::Return { value: reg(0) },
14350 ],
14351 Vec::new(),
14352 ),
14353 function(
14354 1,
14355 2,
14356 vec![
14357 Instruction::LoadThis { dst: reg(1) },
14358 Instruction::StoreGlobal {
14359 name: cid(2),
14360 value: reg(1),
14361 },
14362 Instruction::StoreGlobal {
14363 name: cid(3),
14364 value: reg(0),
14365 },
14366 Instruction::Return { value: reg(0) },
14367 ],
14368 Vec::new(),
14369 ),
14370 function(
14371 0,
14372 1,
14373 vec![
14374 Instruction::LoadConst {
14375 dst: reg(0),
14376 constant: cid(5),
14377 },
14378 Instruction::Throw { value: reg(0) },
14379 ],
14380 Vec::new(),
14381 ),
14382 ],
14383 )
14384 }
14385
14386 fn timer_fn(machine: &mut Machine<'_, TimerTestHost>, index: u32) -> Value {
14387 machine
14388 .allocate(HeapEntry::Function {
14389 module: ModuleId::new(0),
14390 function: FunctionId::new(index),
14391 captures: Vec::new(),
14392 properties: PropertyMap::default(),
14393 prototype: Some(machine.intrinsics.function_prototype),
14394 extensible: true,
14395 })
14396 .unwrap()
14397 }
14398
14399 fn read_global(machine: &Machine<'_, TimerTestHost>, name: &str) -> Option<Value> {
14400 machine.globals.get(&EcmaString::from_utf8(name)).copied()
14401 }
14402
14403 fn set_timeout_global(machine: &Machine<'_, TimerTestHost>) -> Value {
14404 machine
14405 .intrinsics
14406 .global("setTimeout")
14407 .expect("setTimeout is installed")
14408 }
14409
14410 fn schedule_nested_timer(
14411 machine: &mut Machine<'_, TimerTestHost>,
14412 _this: Value,
14413 _args: &[Value],
14414 _constructing: bool,
14415 ) -> Result<BuiltinOutcome, EvalFailure> {
14416 let callback = machine
14417 .globals
14418 .get(&EcmaString::from_utf8("nestedCallback"))
14419 .copied()
14420 .expect("test installs nested callback");
14421 let set_timeout = set_timeout_global(machine);
14422 machine.call_value(set_timeout, Value::UNDEFINED, &[callback, Value::int32(1)])?;
14423 Ok(BuiltinOutcome::Value(Value::UNDEFINED))
14424 }
14425
14426 fn timer_native(
14427 machine: &mut Machine<'_, TimerTestHost>,
14428 name: &'static str,
14429 handler: crate::intrinsics::BuiltinHandler<TimerTestHost>,
14430 ) -> Value {
14431 let id = machine
14432 .intrinsics
14433 .builtins
14434 .register(crate::intrinsics::BuiltinDef {
14435 name,
14436 length: 0,
14437 handler,
14438 });
14439 crate::intrinsics::native_function(&mut machine.heap, id, name, 0)
14440 }
14441
14442 #[test]
14443 fn timers_are_absent_without_the_capability() {
14444 let program = timer_program();
14445 let mut host = TestHost;
14446 let mut machine = Machine::new(&program, &mut host, Limits::default());
14447 machine.frames.clear();
14448 machine.live_registers = 0;
14449 assert!(machine.intrinsics.global("setTimeout").is_none());
14450 assert!(machine.intrinsics.global("clearTimeout").is_none());
14451 assert!(!machine.has_pending_timers());
14452 assert_eq!(
14453 machine.run_one_expired_timer().unwrap(),
14454 TimerRun::default()
14455 );
14456 assert!(!machine.wait_for_timer_expiry().unwrap());
14457 }
14458
14459 #[test]
14460 fn set_timeout_rejects_a_non_callable_callback_before_coercion() {
14461 let program = timer_program();
14462 let mut host = TimerTestHost::default();
14463 let shared = host.provider.state.clone();
14464 let mut machine = Machine::new(&program, &mut host, Limits::default());
14465 machine.frames.clear();
14466 machine.live_registers = 0;
14467 let set_timeout = set_timeout_global(&machine);
14468 let failure = machine
14469 .call_value(
14470 set_timeout,
14471 Value::UNDEFINED,
14472 &[Value::int32(3), Value::int32(5)],
14473 )
14474 .unwrap_err();
14475 assert!(matches!(
14476 failure,
14477 EvalFailure::Throw(ThrowOrigin::TypeError { .. })
14478 ));
14479 assert!(shared.borrow().scheduled.is_empty());
14481 assert!(!machine.has_pending_timers());
14482 }
14483
14484 #[test]
14485 fn set_timeout_clamps_and_truncates_like_node() {
14486 let program = timer_program();
14487 let mut host = TimerTestHost::default();
14488 let shared = host.provider.state.clone();
14489 let mut machine = Machine::new(&program, &mut host, Limits::default());
14490 machine.frames.clear();
14491 machine.live_registers = 0;
14492 let set_timeout = set_timeout_global(&machine);
14493 let callback = timer_fn(&mut machine, 1);
14494 for delay in [
14495 Value::int32(0),
14496 Value::number(-5.0),
14497 Value::number(f64::NAN),
14498 Value::number(2_147_483_648.0),
14499 Value::int32(2_147_483_647),
14500 Value::number(3.9),
14501 ] {
14502 machine
14503 .call_value(set_timeout, Value::UNDEFINED, &[callback, delay])
14504 .unwrap();
14505 }
14506 let delays: Vec<u32> = shared.borrow().scheduled.iter().map(|(_, d)| *d).collect();
14507 assert_eq!(delays, vec![1, 1, 1, 1, 2_147_483_647, 3]);
14508 let ids: Vec<u64> = shared
14510 .borrow()
14511 .scheduled
14512 .iter()
14513 .map(|(id, _)| *id)
14514 .collect();
14515 assert_eq!(ids, vec![1, 2, 3, 4, 5, 6]);
14516 }
14517
14518 #[test]
14519 fn same_deadline_timers_run_in_registration_order_despite_reverse_reports() {
14520 let program = timer_program();
14521 let mut host = TimerTestHost::default();
14522 let shared = host.provider.state.clone();
14523 let mut machine = Machine::new(&program, &mut host, Limits::default());
14524 machine.frames.clear();
14525 machine.live_registers = 0;
14526 let set_timeout = set_timeout_global(&machine);
14527 let a = timer_fn(&mut machine, 1);
14528 let b = timer_fn(&mut machine, 2);
14529 machine
14530 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(5)])
14531 .unwrap();
14532 machine
14533 .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(5)])
14534 .unwrap();
14535 shared.borrow_mut().reports.push_back(TimerWakeup {
14537 id: 2,
14538 deadline_ms: 5,
14539 });
14540 let first = machine.run_one_expired_timer().unwrap();
14541 assert_eq!(first.executed, 1);
14542 assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14543 assert_eq!(read_global(&machine, "b"), None);
14544 let second = machine.run_one_expired_timer().unwrap();
14545 assert_eq!(second.executed, 1);
14546 assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14547 assert!(!machine.has_pending_timers());
14548 }
14549
14550 #[test]
14551 fn a_shorter_deadline_beats_an_older_sequence() {
14552 let program = timer_program();
14553 let mut host = TimerTestHost::default();
14554 let shared = host.provider.state.clone();
14555 let mut machine = Machine::new(&program, &mut host, Limits::default());
14556 machine.frames.clear();
14557 machine.live_registers = 0;
14558 let set_timeout = set_timeout_global(&machine);
14559 let a = timer_fn(&mut machine, 1);
14560 let b = timer_fn(&mut machine, 2);
14561 machine
14562 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(5)])
14563 .unwrap();
14564 machine
14565 .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(3)])
14566 .unwrap();
14567 shared.borrow_mut().reports.push_back(TimerWakeup {
14568 id: 1,
14569 deadline_ms: 5,
14570 });
14571 machine.run_one_expired_timer().unwrap();
14572 assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14573 assert_eq!(read_global(&machine, "a"), None);
14574 }
14575
14576 #[test]
14577 fn clear_timeout_prevents_a_ready_timer_and_ignores_stale_ids() {
14578 let program = timer_program();
14579 let mut host = TimerTestHost::default();
14580 let shared = host.provider.state.clone();
14581 let mut machine = Machine::new(&program, &mut host, Limits::default());
14582 machine.frames.clear();
14583 machine.live_registers = 0;
14584 let set_timeout = set_timeout_global(&machine);
14585 let clear_timeout = machine.intrinsics.global("clearTimeout").unwrap();
14586 let a = timer_fn(&mut machine, 1);
14587 let b = timer_fn(&mut machine, 2);
14588 let handle_a = machine
14589 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14590 .unwrap();
14591 machine
14592 .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(3)])
14593 .unwrap();
14594 shared.borrow_mut().reports.push_back(TimerWakeup {
14596 id: 1,
14597 deadline_ms: 3,
14598 });
14599 machine
14600 .call_value(clear_timeout, Value::UNDEFINED, &[handle_a])
14601 .unwrap();
14602 assert!(shared.borrow().cancelled.contains(&1));
14603 machine
14605 .call_value(clear_timeout, Value::UNDEFINED, &[Value::int32(1)])
14606 .unwrap();
14607 shared.borrow_mut().reports.push_back(TimerWakeup {
14608 id: 2,
14609 deadline_ms: 3,
14610 });
14611 let run = machine.run_one_expired_timer().unwrap();
14612 assert_eq!(run.executed, 1);
14613 assert_eq!(read_global(&machine, "a"), None);
14614 assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14615 }
14616
14617 #[test]
14618 fn clear_timeout_accepts_a_direct_positive_integer_id() {
14619 let program = timer_program();
14620 let mut host = TimerTestHost::default();
14621 let shared = host.provider.state.clone();
14622 let mut machine = Machine::new(&program, &mut host, Limits::default());
14623 machine.frames.clear();
14624 machine.live_registers = 0;
14625 let set_timeout = set_timeout_global(&machine);
14626 let clear_timeout = machine.intrinsics.global("clearTimeout").unwrap();
14627 let a = timer_fn(&mut machine, 1);
14628 machine
14629 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14630 .unwrap();
14631 machine
14632 .call_value(clear_timeout, Value::UNDEFINED, &[Value::int32(1)])
14633 .unwrap();
14634 assert!(!machine.has_pending_timers());
14635 shared.borrow_mut().reports.push_back(TimerWakeup {
14636 id: 1,
14637 deadline_ms: 3,
14638 });
14639 assert_eq!(machine.run_one_expired_timer().unwrap().executed, 0);
14640
14641 machine.next_timer_id = Some(u64::MAX);
14642 let handle = machine
14643 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14644 .unwrap();
14645 machine
14646 .call_value(
14647 clear_timeout,
14648 Value::UNDEFINED,
14649 &[Value::number(u64::MAX as f64)],
14650 )
14651 .unwrap();
14652 assert!(machine.has_pending_timers());
14653 machine
14654 .call_value(clear_timeout, Value::UNDEFINED, &[handle])
14655 .unwrap();
14656 assert!(!machine.has_pending_timers());
14657 machine
14659 .call_value(clear_timeout, Value::UNDEFINED, &[Value::UNDEFINED])
14660 .unwrap();
14661 }
14662
14663 #[test]
14664 fn timer_callback_receives_trailing_args_and_the_handle_as_this() {
14665 let program = timer_program();
14666 let mut host = TimerTestHost::default();
14667 let shared = host.provider.state.clone();
14668 let mut machine = Machine::new(&program, &mut host, Limits::default());
14669 machine.frames.clear();
14670 machine.live_registers = 0;
14671 let set_timeout = set_timeout_global(&machine);
14672 let callback = timer_fn(&mut machine, 3);
14673 let handle = machine
14674 .call_value(
14675 set_timeout,
14676 Value::UNDEFINED,
14677 &[callback, Value::int32(1), Value::int32(42)],
14678 )
14679 .unwrap();
14680 shared.borrow_mut().reports.push_back(TimerWakeup {
14681 id: 1,
14682 deadline_ms: 1,
14683 });
14684 machine.run_one_expired_timer().unwrap();
14685 assert_eq!(read_global(&machine, "this_seen"), Some(handle));
14686 assert_eq!(read_global(&machine, "arg_seen"), Some(Value::int32(42)));
14687 }
14688
14689 #[test]
14690 fn a_callback_created_timer_waits_for_a_later_checkpoint() {
14691 let program = timer_program();
14692 let mut host = TimerTestHost::default();
14693 let shared = host.provider.state.clone();
14694 let mut machine = Machine::new(&program, &mut host, Limits::default());
14695 machine.frames.clear();
14696 machine.live_registers = 0;
14697 let set_timeout = set_timeout_global(&machine);
14698 let nested = timer_fn(&mut machine, 2);
14699 machine
14700 .globals
14701 .insert(EcmaString::from_utf8("nestedCallback"), nested);
14702 let creator = timer_native(&mut machine, "schedule nested", schedule_nested_timer);
14703 machine
14704 .call_value(set_timeout, Value::UNDEFINED, &[creator, Value::int32(1)])
14705 .unwrap();
14706 shared.borrow_mut().reports.push_back(TimerWakeup {
14707 id: 1,
14708 deadline_ms: 1,
14709 });
14710 assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14711 assert_eq!(read_global(&machine, "b"), None);
14712 assert!(machine.has_pending_timers());
14713 shared.borrow_mut().reports.push_back(TimerWakeup {
14716 id: 2,
14717 deadline_ms: 1,
14718 });
14719 assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14720 assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14721 }
14722
14723 #[test]
14724 fn timer_callback_throw_is_reported_and_a_runtime_failure_propagates() {
14725 let program = timer_program();
14726 let mut host = TimerTestHost::default();
14727 let shared = host.provider.state.clone();
14728 let mut machine = Machine::new(&program, &mut host, Limits::default());
14729 machine.frames.clear();
14730 machine.live_registers = 0;
14731 let set_timeout = set_timeout_global(&machine);
14732 let thrower = timer_fn(&mut machine, 4);
14733 machine
14734 .call_value(set_timeout, Value::UNDEFINED, &[thrower, Value::int32(1)])
14735 .unwrap();
14736 shared.borrow_mut().reports.push_back(TimerWakeup {
14737 id: 1,
14738 deadline_ms: 1,
14739 });
14740 let run = machine.run_one_expired_timer().unwrap();
14741 assert_eq!(run.executed, 1);
14742 assert_eq!(
14743 run.uncaught,
14744 vec![CallbackException {
14745 value: Value::int32(7),
14746 origin: ThrowOrigin::Bytecode
14747 }]
14748 );
14749
14750 let another = timer_fn(&mut machine, 1);
14752 machine
14753 .call_value(set_timeout, Value::UNDEFINED, &[another, Value::int32(1)])
14754 .unwrap();
14755 shared.borrow_mut().reports.push_back(TimerWakeup {
14756 id: 2,
14757 deadline_ms: 1,
14758 });
14759 machine.fuel = 1;
14760 let error = machine.run_one_expired_timer().unwrap_err();
14761 assert!(matches!(error.kind, RuntimeErrorKind::FuelExhausted { .. }));
14762 }
14763
14764 #[test]
14765 fn a_timer_checkpoint_never_drains_microtasks() {
14766 let program = timer_program();
14767 let mut host = TimerTestHost::default();
14768 let shared = host.provider.state.clone();
14769 let mut machine = Machine::new(&program, &mut host, Limits::default());
14770 machine.frames.clear();
14771 machine.live_registers = 0;
14772 let set_timeout = set_timeout_global(&machine);
14773 let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14774 let a = timer_fn(&mut machine, 1);
14775 let b = timer_fn(&mut machine, 2);
14776 machine
14777 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14778 .unwrap();
14779 machine.call_value(queue, Value::UNDEFINED, &[b]).unwrap();
14780 shared.borrow_mut().reports.push_back(TimerWakeup {
14781 id: 1,
14782 deadline_ms: 1,
14783 });
14784 let run = machine.run_one_expired_timer().unwrap();
14785 assert_eq!(run.executed, 1);
14786 assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14787 assert_eq!(read_global(&machine, "b"), None);
14788 assert_eq!(machine.microtasks.len(), 1);
14789 machine.drain_microtasks().unwrap();
14790 assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14791 }
14792
14793 #[test]
14794 fn timer_reentry_capacity_and_fuel_preserve_state() {
14795 let program = timer_program();
14796 let mut host = TimerTestHost::default();
14797 let shared = host.provider.state.clone();
14798 let mut machine = Machine::new(
14799 &program,
14800 &mut host,
14801 Limits {
14802 max_timers: 1,
14803 ..Limits::default()
14804 },
14805 );
14806 machine.frames.clear();
14807 machine.live_registers = 0;
14808 let set_timeout = set_timeout_global(&machine);
14809 let a = timer_fn(&mut machine, 1);
14810 let b = timer_fn(&mut machine, 2);
14811 machine
14812 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14813 .unwrap();
14814 let capacity = machine
14816 .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(1)])
14817 .unwrap_err();
14818 assert!(matches!(
14819 capacity,
14820 EvalFailure::Runtime(RuntimeErrorKind::TimerCapacityExceeded { limit: 1 })
14821 ));
14822 assert_eq!(shared.borrow().scheduled.len(), 1);
14823
14824 shared.borrow_mut().reports.push_back(TimerWakeup {
14826 id: 1,
14827 deadline_ms: 1,
14828 });
14829 machine.timer_checkpoint_active = true;
14830 let fuel = machine.fuel;
14831 let reentry = machine.run_one_expired_timer().unwrap_err();
14832 assert!(matches!(
14833 reentry.kind,
14834 RuntimeErrorKind::TimerCheckpointReentry
14835 ));
14836 assert_eq!(machine.fuel, fuel);
14837 machine.timer_checkpoint_active = false;
14838
14839 machine.fuel = 0;
14841 let exhausted = machine.run_one_expired_timer().unwrap_err();
14842 assert!(matches!(
14843 exhausted.kind,
14844 RuntimeErrorKind::FuelExhausted { .. }
14845 ));
14846 assert!(machine.has_pending_timers());
14847 machine.fuel = 100;
14848 assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14849 assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14850 }
14851
14852 #[test]
14853 fn a_failed_schedule_never_reuses_its_timer_id() {
14854 let program = timer_program();
14855 let mut host = TimerTestHost::default();
14856 let shared = host.provider.state.clone();
14857 let mut machine = Machine::new(&program, &mut host, Limits::default());
14858 machine.frames.clear();
14859 machine.live_registers = 0;
14860 let set_timeout = set_timeout_global(&machine);
14861 let a = timer_fn(&mut machine, 1);
14862 shared.borrow_mut().fail_schedule = true;
14863 let failure = machine
14864 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14865 .unwrap_err();
14866 assert!(matches!(
14867 failure,
14868 EvalFailure::Runtime(RuntimeErrorKind::TimerProviderFailure { .. })
14869 ));
14870 shared.borrow_mut().fail_schedule = false;
14871 machine
14872 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14873 .unwrap();
14874 let ids: Vec<u64> = shared
14875 .borrow()
14876 .scheduled
14877 .iter()
14878 .map(|(id, _)| *id)
14879 .collect();
14880 assert_eq!(ids, vec![1, 2]);
14881 }
14882
14883 #[test]
14884 fn wait_for_timer_expiry_promotes_a_reported_timer() {
14885 let program = timer_program();
14886 let mut host = TimerTestHost::default();
14887 let shared = host.provider.state.clone();
14888 let mut machine = Machine::new(&program, &mut host, Limits::default());
14889 machine.frames.clear();
14890 machine.live_registers = 0;
14891 assert!(!machine.wait_for_timer_expiry().unwrap());
14892 let set_timeout = set_timeout_global(&machine);
14893 let a = timer_fn(&mut machine, 1);
14894 machine
14895 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14896 .unwrap();
14897 shared.borrow_mut().reports.push_back(TimerWakeup {
14898 id: 1,
14899 deadline_ms: 1,
14900 });
14901 assert!(machine.wait_for_timer_expiry().unwrap());
14902 assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14903 assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14904 }
14905}