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> {
1402 let execution = self.evaluate()?;
1403 self.run_to_quiescence()?;
1404 Ok(execution)
1405 }
1406
1407 pub fn evaluate(&mut self) -> Result<Execution, RuntimeError> {
1412 if let Some(program) = self.program {
1413 let entry = program.entry();
1414 self.frames.clear();
1415 self.live_registers = 0;
1416 self.instantiate_modules()?;
1417 return self.evaluate_module(entry)?.ok_or_else(|| {
1418 self.program_error(
1419 entry,
1420 RuntimeErrorKind::InvalidVerifiedProgram {
1421 module: entry,
1422 instruction: Instruction::Halt,
1423 },
1424 )
1425 });
1426 }
1427 Ok(self
1428 .run_loop(0)?
1429 .expect("the entry frame completes before the run loop stops"))
1430 }
1431
1432 pub fn run_one_expired_timer(&mut self) -> Result<TimerRun, RuntimeError> {
1438 if self.timer_checkpoint_active {
1439 return Err(self.checkpoint_error(RuntimeErrorKind::TimerCheckpointReentry));
1440 }
1441 self.timer_checkpoint_active = true;
1442 let result = (|| {
1443 self.poll_timer_expiries()
1444 .map_err(|kind| self.checkpoint_error(kind))?;
1445 let Some(order) = self.ready_timers.first().copied() else {
1446 return Ok(TimerRun::default());
1447 };
1448 let Some(id) = self.timers.iter().find_map(|(id, timer)| {
1449 ((timer.deadline_ms, timer.sequence) == order).then_some(*id)
1450 }) else {
1451 return Err(self.checkpoint_error(RuntimeErrorKind::InvalidValue {
1452 value: Value::UNDEFINED,
1453 }));
1454 };
1455 self.consume_fuel(1)
1457 .map_err(|kind| self.checkpoint_error(kind))?;
1458 self.ready_timers.remove(&order);
1459 let timer = self
1460 .timers
1461 .remove(&id)
1462 .expect("ready timer remains live until after fuel charging");
1463 let mut report = TimerRun {
1464 executed: 1,
1465 uncaught: Vec::new(),
1466 };
1467 match self.call_value(timer.callback, timer.handle, &timer.arguments) {
1468 Ok(_) => {}
1469 Err(EvalFailure::Runtime(kind)) => {
1470 return Err(self.checkpoint_error(kind));
1471 }
1472 Err(failure) => {
1473 let (value, origin) =
1474 self.promise_rejection_value(failure)
1475 .map_err(|failure| match failure {
1476 EvalFailure::Runtime(kind) => self.checkpoint_error(kind),
1477 _ => self.checkpoint_error(RuntimeErrorKind::InvalidValue {
1478 value: timer.callback,
1479 }),
1480 })?;
1481 report.uncaught.try_reserve(1).map_err(|_| {
1482 self.checkpoint_error(RuntimeErrorKind::HeapByteLimitExceeded {
1483 limit: self.limits.max_heap_bytes,
1484 })
1485 })?;
1486 report.uncaught.push(CallbackException { value, origin });
1487 }
1488 }
1489 Ok(report)
1490 })();
1491 self.timer_checkpoint_active = false;
1492 result
1493 }
1494
1495 pub fn wait_for_timer_expiry(&mut self) -> Result<bool, RuntimeError> {
1498 if self.timer_checkpoint_active {
1499 return Err(self.checkpoint_error(RuntimeErrorKind::TimerCheckpointReentry));
1500 }
1501 if !self.ready_timers.is_empty() {
1502 return Ok(true);
1503 }
1504 self.timer_checkpoint_active = true;
1505 let result = (|| {
1506 let wakeup = match self.host.timers() {
1507 Some(provider) => provider.wait_expired(),
1508 None => return Ok(false),
1509 }
1510 .map_err(|error| {
1511 self.checkpoint_error(RuntimeErrorKind::TimerProviderFailure {
1512 message: error.to_string(),
1513 })
1514 })?;
1515 if let Some(wakeup) = wakeup {
1516 self.promote_timer_wakeup(wakeup);
1517 }
1518 Ok(!self.ready_timers.is_empty())
1519 })();
1520 self.timer_checkpoint_active = false;
1521 result
1522 }
1523
1524 #[must_use]
1526 pub fn has_pending_timers(&self) -> bool {
1527 !self.timers.is_empty()
1528 }
1529
1530 pub fn run_to_quiescence(&mut self) -> Result<(), RuntimeError> {
1549 self.drain_microtasks_automatic()?;
1550 while self.has_pending_timers() {
1551 if !self.wait_for_timer_expiry()? {
1552 let retry = self
1553 .host
1554 .timers()
1555 .is_some_and(|provider| provider.has_pending());
1556 if !retry {
1557 return Err(self.checkpoint_error(RuntimeErrorKind::TimerProviderFailure {
1558 message: "the timer provider lost a live machine timer".to_owned(),
1559 }));
1560 }
1561 continue;
1562 }
1563 let run = self.run_one_expired_timer()?;
1564 if let Some(exception) = run.uncaught.into_iter().next() {
1565 return Err(self.checkpoint_error(RuntimeErrorKind::UncaughtThrow {
1566 value: exception.value,
1567 origin: exception.origin,
1568 }));
1569 }
1570 self.drain_microtasks_automatic()?;
1571 }
1572 Ok(())
1573 }
1574
1575 pub(crate) fn schedule_timeout(
1576 &mut self,
1577 callback: Value,
1578 delay_ms: u32,
1579 arguments: Vec<Value>,
1580 ) -> Result<Value, EvalFailure> {
1581 if self.timers.len() >= self.limits.max_timers {
1582 return Err(EvalFailure::Runtime(
1583 RuntimeErrorKind::TimerCapacityExceeded {
1584 limit: self.limits.max_timers,
1585 },
1586 ));
1587 }
1588 let id = self.next_timer_id.take().ok_or(EvalFailure::Runtime(
1589 RuntimeErrorKind::TimerCapacityExceeded {
1590 limit: self.limits.max_timers,
1591 },
1592 ))?;
1593 self.next_timer_id = id.checked_add(1);
1594 let sequence = self.next_timer_sequence.take().ok_or(EvalFailure::Runtime(
1595 RuntimeErrorKind::TimerCapacityExceeded {
1596 limit: self.limits.max_timers,
1597 },
1598 ))?;
1599 self.next_timer_sequence = sequence.checked_add(1);
1600 let deadline_ms = self
1601 .host
1602 .timers()
1603 .ok_or(EvalFailure::Runtime(
1604 RuntimeErrorKind::TimerProviderFailure {
1605 message: "timer capability is unavailable".to_owned(),
1606 },
1607 ))?
1608 .schedule(id, delay_ms)
1609 .map_err(|error| {
1610 EvalFailure::Runtime(RuntimeErrorKind::TimerProviderFailure {
1611 message: error.to_string(),
1612 })
1613 })?;
1614 let handle = match self.allocate(HeapEntry::Timeout {
1615 id,
1616 properties: PropertyMap::default(),
1617 prototype: Some(self.intrinsics.object_prototype),
1618 extensible: true,
1619 }) {
1620 Ok(handle) => handle,
1621 Err(kind) => {
1622 if let Some(provider) = self.host.timers() {
1623 let _ = provider.cancel(id);
1624 }
1625 return Err(EvalFailure::Runtime(kind));
1626 }
1627 };
1628 self.timers.insert(
1629 id,
1630 TimerRecord {
1631 callback,
1632 arguments,
1633 handle,
1634 deadline_ms,
1635 sequence,
1636 },
1637 );
1638 Ok(handle)
1639 }
1640
1641 pub(crate) fn clear_timeout(&mut self, handle: Value) -> Result<(), EvalFailure> {
1642 let id = match handle.decode() {
1643 Some(Decoded::Int32(raw)) if (raw as i32) > 0 => Some(u64::from(raw)),
1644 Some(Decoded::Number(number))
1645 if number.is_finite()
1646 && number > 0.0
1647 && number.fract() == 0.0
1648 && number < u64::MAX as f64 =>
1649 {
1650 Some(number as u64)
1651 }
1652 Some(Decoded::HeapRef(_)) => {
1653 self.runtime_slot(handle)
1654 .ok()
1655 .flatten()
1656 .and_then(|index| match &self.heap[index] {
1657 HeapEntry::Timeout { id, .. } => Some(*id),
1658 _ => None,
1659 })
1660 }
1661 _ => None,
1662 };
1663 let Some(id) = id else {
1664 return Ok(());
1665 };
1666 let Some(timer) = self.timers.remove(&id) else {
1667 return Ok(());
1668 };
1669 self.ready_timers
1670 .remove(&(timer.deadline_ms, timer.sequence));
1671 if let Some(provider) = self.host.timers() {
1672 provider.cancel(id).map_err(|error| {
1673 EvalFailure::Runtime(RuntimeErrorKind::TimerProviderFailure {
1674 message: error.to_string(),
1675 })
1676 })?;
1677 }
1678 Ok(())
1679 }
1680
1681 fn poll_timer_expiries(&mut self) -> Result<(), RuntimeErrorKind> {
1682 let mut wakeups = Vec::new();
1683 let Some(provider) = self.host.timers() else {
1684 return Ok(());
1685 };
1686 provider.poll_expired(&mut wakeups).map_err(|error| {
1687 RuntimeErrorKind::TimerProviderFailure {
1688 message: error.to_string(),
1689 }
1690 })?;
1691 if let Some(wakeup) = wakeups
1694 .into_iter()
1695 .filter(|wakeup| self.timers.contains_key(&wakeup.id))
1696 .max_by_key(|wakeup| wakeup.deadline_ms)
1697 {
1698 self.promote_timer_wakeup(wakeup);
1699 }
1700 Ok(())
1701 }
1702
1703 fn promote_timer_wakeup(&mut self, wakeup: TimerWakeup) {
1704 if !self.timers.contains_key(&wakeup.id) {
1707 return;
1708 }
1709 let watermark = self.timer_watermark.map_or(wakeup.deadline_ms, |current| {
1710 current.max(wakeup.deadline_ms)
1711 });
1712 self.timer_watermark = Some(watermark);
1713 for timer in self.timers.values() {
1714 if timer.deadline_ms <= watermark {
1715 self.ready_timers
1716 .insert((timer.deadline_ms, timer.sequence));
1717 }
1718 }
1719 }
1720
1721 pub fn drain_microtasks(&mut self) -> Result<MicrotaskDrain, RuntimeError> {
1727 self.drain_microtasks_core(true)
1728 }
1729
1730 fn drain_microtasks_automatic(&mut self) -> Result<(), RuntimeError> {
1735 let report = self.drain_microtasks_core(false)?;
1736 let Some(exception) = report.uncaught.into_iter().next() else {
1737 return Ok(());
1738 };
1739 Err(self.checkpoint_error(RuntimeErrorKind::UncaughtThrow {
1740 value: exception.value,
1741 origin: exception.origin,
1742 }))
1743 }
1744
1745 fn drain_microtasks_core(
1752 &mut self,
1753 collect_uncaught: bool,
1754 ) -> Result<MicrotaskDrain, RuntimeError> {
1755 if self.microtask_drain_active {
1756 return Err(self.checkpoint_error(RuntimeErrorKind::MicrotaskDrainReentry));
1757 }
1758 self.microtask_drain_active = true;
1759 let result = (|| {
1760 let mut report = MicrotaskDrain::default();
1761 while self.microtasks.front().is_some() {
1762 self.consume_fuel(1)
1763 .map_err(|kind| self.checkpoint_error(kind))?;
1764 let job = self
1765 .microtasks
1766 .pop_front()
1767 .expect("the queued microtask remains present after fuel charging");
1768 report.executed = report.executed.saturating_add(1);
1769 let Some(exception) = self
1770 .execute_microtask_job(job)
1771 .map_err(|kind| self.checkpoint_error(kind))?
1772 else {
1773 continue;
1774 };
1775 report.uncaught.try_reserve(1).map_err(|_| {
1776 self.checkpoint_error(RuntimeErrorKind::HeapByteLimitExceeded {
1777 limit: self.limits.max_heap_bytes,
1778 })
1779 })?;
1780 report.uncaught.push(exception);
1781 if !collect_uncaught {
1782 break;
1783 }
1784 }
1785 Ok(report)
1786 })();
1787 self.microtask_drain_active = false;
1788 result
1789 }
1790
1791 fn checkpoint_error(&self, kind: RuntimeErrorKind) -> RuntimeError {
1792 let function = self.module.entry();
1793 let instruction = self.module.functions()[function.get() as usize]
1794 .code()
1795 .first()
1796 .copied()
1797 .unwrap_or(Instruction::Halt);
1798 RuntimeError {
1799 kind,
1800 function,
1801 pc: Pc::new(0),
1802 source: RuntimeSource {
1803 function_name: None,
1804 instruction,
1805 },
1806 }
1807 }
1808
1809 fn execute_microtask_job(
1814 &mut self,
1815 job: MicrotaskJob,
1816 ) -> Result<Option<CallbackException>, RuntimeErrorKind> {
1817 match job {
1818 MicrotaskJob::Reaction {
1819 reaction,
1820 value,
1821 origin,
1822 } => self
1823 .execute_promise_reaction(reaction, value, origin)
1824 .map(|()| None),
1825 MicrotaskJob::Thenable {
1826 promise,
1827 thenable,
1828 then,
1829 } => self
1830 .execute_thenable_job(promise, thenable, then)
1831 .map(|()| None),
1832 MicrotaskJob::Callback { callback } => self.execute_callback_microtask(callback),
1833 }
1834 }
1835
1836 fn execute_callback_microtask(
1837 &mut self,
1838 callback: Value,
1839 ) -> Result<Option<CallbackException>, RuntimeErrorKind> {
1840 match self.call_value(callback, Value::UNDEFINED, &[]) {
1841 Ok(_) => Ok(None),
1842 Err(EvalFailure::Runtime(kind)) => Err(kind),
1843 Err(failure) => {
1844 let (value, origin) =
1845 self.promise_rejection_value(failure)
1846 .map_err(|failure| match failure {
1847 EvalFailure::Runtime(kind) => kind,
1848 _ => RuntimeErrorKind::InvalidValue { value: callback },
1849 })?;
1850 Ok(Some(CallbackException { value, origin }))
1851 }
1852 }
1853 }
1854
1855 fn execute_thenable_job(
1856 &mut self,
1857 promise: Value,
1858 thenable: Value,
1859 then: Value,
1860 ) -> Result<(), RuntimeErrorKind> {
1861 let record = self
1862 .create_promise_resolver(promise)
1863 .map_err(|failure| match failure {
1864 EvalFailure::Runtime(kind) => kind,
1865 _ => RuntimeErrorKind::InvalidValue { value: promise },
1866 })?;
1867 let (resolve_target, reject_target) = self.intrinsics.builtins.promise_resolver_targets();
1868 let resolve = self
1869 .create_promise_resolver_function(resolve_target, record)
1870 .map_err(|failure| match failure {
1871 EvalFailure::Runtime(kind) => kind,
1872 _ => RuntimeErrorKind::InvalidValue { value: record },
1873 })?;
1874 let reject = self
1875 .create_promise_resolver_function(reject_target, record)
1876 .map_err(|failure| match failure {
1877 EvalFailure::Runtime(kind) => kind,
1878 _ => RuntimeErrorKind::InvalidValue { value: record },
1879 })?;
1880 match self.call_value(then, thenable, &[resolve, reject]) {
1881 Ok(_) => Ok(()),
1882 Err(EvalFailure::Runtime(kind)) => Err(kind),
1883 Err(failure) => self
1884 .reject_promise_resolver_failure(record, failure)
1885 .map_err(|failure| match failure {
1886 EvalFailure::Runtime(kind) => kind,
1887 _ => RuntimeErrorKind::InvalidValue { value: record },
1888 }),
1889 }
1890 }
1891
1892 fn execute_promise_reaction(
1893 &mut self,
1894 reaction: PromiseReaction,
1895 value: Value,
1896 origin: ThrowOrigin,
1897 ) -> Result<(), RuntimeErrorKind> {
1898 match reaction {
1899 PromiseReaction::Fulfilled { handler, derived } => self.execute_promise_handler(
1900 handler,
1901 derived,
1902 value,
1903 origin,
1904 PromiseCompletion::Fulfilled,
1905 ),
1906 PromiseReaction::Rejected { handler, derived } => self.execute_promise_handler(
1907 handler,
1908 derived,
1909 value,
1910 origin,
1911 PromiseCompletion::Rejected,
1912 ),
1913 PromiseReaction::Finally {
1914 handler,
1915 derived,
1916 completion,
1917 } => self.execute_promise_finally(handler, derived, value, origin, completion),
1918 PromiseReaction::AsyncFulfill { activation } => {
1919 self.resume_async(activation, value, None)
1920 }
1921 PromiseReaction::AsyncReject { activation } => {
1922 self.resume_async(activation, value, Some(origin))
1923 }
1924 }
1925 }
1926
1927 fn execute_promise_handler(
1928 &mut self,
1929 handler: Value,
1930 derived: Value,
1931 value: Value,
1932 origin: ThrowOrigin,
1933 completion: PromiseCompletion,
1934 ) -> Result<(), RuntimeErrorKind> {
1935 if !self.is_callable(handler).map_err(|failure| match failure {
1936 EvalFailure::Runtime(kind) => kind,
1937 _ => RuntimeErrorKind::InvalidValue { value: handler },
1938 })? {
1939 return match completion {
1940 PromiseCompletion::Fulfilled => self.resolve_promise(derived, value),
1941 PromiseCompletion::Rejected => self.reject_promise(derived, value, origin),
1942 };
1943 }
1944 match self.call_value(handler, Value::UNDEFINED, &[value]) {
1945 Ok(result) => self.resolve_promise(derived, result),
1946 Err(EvalFailure::Runtime(kind)) => Err(kind),
1947 Err(failure) => self
1948 .reject_promise_failure(derived, failure)
1949 .map_err(|failure| match failure {
1950 EvalFailure::Runtime(kind) => kind,
1951 _ => RuntimeErrorKind::InvalidValue { value: derived },
1952 }),
1953 }
1954 }
1955
1956 fn execute_promise_finally(
1957 &mut self,
1958 handler: Value,
1959 derived: Value,
1960 value: Value,
1961 origin: ThrowOrigin,
1962 completion: PromiseCompletion,
1963 ) -> Result<(), RuntimeErrorKind> {
1964 if !self.is_callable(handler).map_err(|failure| match failure {
1965 EvalFailure::Runtime(kind) => kind,
1966 _ => RuntimeErrorKind::InvalidValue { value: handler },
1967 })? {
1968 return match completion {
1969 PromiseCompletion::Fulfilled => self.resolve_promise(derived, value),
1970 PromiseCompletion::Rejected => self.reject_promise(derived, value, origin),
1971 };
1972 }
1973 let cleanup = self.create_promise().map_err(|failure| match failure {
1974 EvalFailure::Runtime(kind) => kind,
1975 _ => RuntimeErrorKind::InvalidValue { value: derived },
1976 })?;
1977 let record = self
1978 .create_promise_finally(derived, value, origin, completion)
1979 .map_err(|failure| match failure {
1980 EvalFailure::Runtime(kind) => kind,
1981 _ => RuntimeErrorKind::InvalidValue { value: derived },
1982 })?;
1983 let (on_fulfilled, on_rejected) = self.intrinsics.builtins.promise_finally_targets();
1984 let on_fulfilled = self
1985 .create_promise_resolver_function(on_fulfilled, record)
1986 .map_err(|failure| match failure {
1987 EvalFailure::Runtime(kind) => kind,
1988 _ => RuntimeErrorKind::InvalidValue { value: record },
1989 })?;
1990 let on_rejected = self
1991 .create_promise_resolver_function(on_rejected, record)
1992 .map_err(|failure| match failure {
1993 EvalFailure::Runtime(kind) => kind,
1994 _ => RuntimeErrorKind::InvalidValue { value: record },
1995 })?;
1996 self.promise_then(cleanup, on_fulfilled, on_rejected)
1997 .map_err(|failure| match failure {
1998 EvalFailure::Runtime(kind) => kind,
1999 _ => RuntimeErrorKind::InvalidValue { value: cleanup },
2000 })?;
2001 match self.call_value(handler, Value::UNDEFINED, &[]) {
2002 Ok(result) => self.resolve_promise(cleanup, result),
2003 Err(EvalFailure::Runtime(kind)) => Err(kind),
2004 Err(failure) => self
2005 .reject_promise_failure(cleanup, failure)
2006 .map_err(|failure| match failure {
2007 EvalFailure::Runtime(kind) => kind,
2008 _ => RuntimeErrorKind::InvalidValue { value: cleanup },
2009 }),
2010 }
2011 }
2012
2013 pub(crate) fn enqueue_microtask_callback(
2014 &mut self,
2015 callback: Value,
2016 ) -> Result<(), EvalFailure> {
2017 self.ensure_microtask_capacity(1)
2018 .map_err(EvalFailure::Runtime)?;
2019 self.microtasks
2020 .push_back(MicrotaskJob::Callback { callback });
2021 Ok(())
2022 }
2023
2024 fn ensure_microtask_capacity(&mut self, additional: usize) -> Result<(), RuntimeErrorKind> {
2025 if self
2026 .microtasks
2027 .len()
2028 .checked_add(additional)
2029 .is_none_or(|length| length > self.limits.max_microtasks)
2030 {
2031 return Err(RuntimeErrorKind::MicrotaskQueueLimitExceeded {
2032 limit: self.limits.max_microtasks,
2033 });
2034 }
2035 self.microtasks.try_reserve(additional).map_err(|_| {
2036 RuntimeErrorKind::HeapByteLimitExceeded {
2037 limit: self.limits.max_heap_bytes,
2038 }
2039 })
2040 }
2041
2042 pub(crate) fn create_promise(&mut self) -> Result<Value, EvalFailure> {
2043 self.allocate(HeapEntry::Promise {
2044 state: PromiseState::Pending {
2045 fulfill_reactions: Vec::new(),
2046 reject_reactions: Vec::new(),
2047 },
2048 properties: PropertyMap::default(),
2049 prototype: Some(self.intrinsics.builtins.promise_prototype()),
2050 extensible: true,
2051 })
2052 .map_err(EvalFailure::Runtime)
2053 }
2054
2055 pub(crate) fn create_promise_resolver(&mut self, promise: Value) -> Result<Value, EvalFailure> {
2056 self.allocate(HeapEntry::PromiseResolver {
2057 promise,
2058 used: false,
2059 })
2060 .map_err(EvalFailure::Runtime)
2061 }
2062
2063 pub(crate) fn create_promise_resolver_function(
2064 &mut self,
2065 target: Value,
2066 record: Value,
2067 ) -> Result<Value, EvalFailure> {
2068 self.allocate(HeapEntry::NativeFunction {
2069 callable: NativeCallable::Bound(Box::new(BoundCallable {
2070 target,
2071 this_value: Value::UNDEFINED,
2072 arguments: vec![record],
2073 })),
2074 properties: PropertyMap::default(),
2075 extensible: true,
2076 })
2077 .map_err(EvalFailure::Runtime)
2078 }
2079
2080 pub(crate) fn resolve_promise_resolver(
2081 &mut self,
2082 record: Value,
2083 value: Value,
2084 ) -> Result<(), EvalFailure> {
2085 if let Some(promise) = self.use_promise_resolver(record)? {
2086 self.resolve_promise(promise, value)
2087 .map_err(EvalFailure::Runtime)?;
2088 }
2089 Ok(())
2090 }
2091
2092 pub(crate) fn reject_promise_resolver(
2093 &mut self,
2094 record: Value,
2095 reason: Value,
2096 ) -> Result<(), EvalFailure> {
2097 if let Some(promise) = self.use_promise_resolver(record)? {
2098 self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2099 .map_err(EvalFailure::Runtime)?;
2100 }
2101 Ok(())
2102 }
2103
2104 pub(crate) fn reject_promise_resolver_failure(
2105 &mut self,
2106 record: Value,
2107 failure: EvalFailure,
2108 ) -> Result<(), EvalFailure> {
2109 if let Some(promise) = self.use_promise_resolver(record)? {
2110 self.reject_promise_failure(promise, failure)?;
2111 }
2112 Ok(())
2113 }
2114
2115 fn use_promise_resolver(&mut self, record: Value) -> Result<Option<Value>, EvalFailure> {
2116 let index = self
2117 .runtime_slot(record)
2118 .map_err(EvalFailure::Runtime)?
2119 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2120 operation: "Promise resolver",
2121 }))?;
2122 let HeapEntry::PromiseResolver { promise, used } = &mut self.heap[index] else {
2123 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2124 operation: "Promise resolver",
2125 }));
2126 };
2127 if *used {
2128 return Ok(None);
2129 }
2130 *used = true;
2131 Ok(Some(*promise))
2132 }
2133
2134 fn charge_promise_reactions(&mut self, count: usize) -> Result<(), EvalFailure> {
2135 let bytes = std::mem::size_of::<PromiseReaction>()
2136 .checked_mul(count)
2137 .ok_or(EvalFailure::Runtime(
2138 RuntimeErrorKind::HeapByteLimitExceeded {
2139 limit: self.limits.max_heap_bytes,
2140 },
2141 ))?;
2142 self.charge_heap(bytes).map_err(EvalFailure::Runtime)
2143 }
2144
2145 pub(crate) fn promise_then(
2146 &mut self,
2147 promise: Value,
2148 on_fulfilled: Value,
2149 on_rejected: Value,
2150 ) -> Result<Value, EvalFailure> {
2151 let index = self
2152 .runtime_slot(promise)
2153 .map_err(EvalFailure::Runtime)?
2154 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2155 operation: "Promise.prototype.then",
2156 }))?;
2157 let settled = match &self.heap[index] {
2158 HeapEntry::Promise {
2159 state: PromiseState::Pending { .. },
2160 ..
2161 } => None,
2162 HeapEntry::Promise {
2163 state: PromiseState::Fulfilled { value },
2164 ..
2165 } => Some((true, *value, ThrowOrigin::Bytecode)),
2166 HeapEntry::Promise {
2167 state: PromiseState::Rejected { reason, origin },
2168 ..
2169 } => Some((false, *reason, *origin)),
2170 _ => {
2171 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2172 operation: "Promise.prototype.then",
2173 }));
2174 }
2175 };
2176 let derived = self.create_promise()?;
2177 if let Some((fulfilled, value, origin)) = settled {
2178 self.ensure_microtask_capacity(1)
2179 .map_err(EvalFailure::Runtime)?;
2180 let reaction = if fulfilled {
2181 PromiseReaction::Fulfilled {
2182 handler: on_fulfilled,
2183 derived,
2184 }
2185 } else {
2186 PromiseReaction::Rejected {
2187 handler: on_rejected,
2188 derived,
2189 }
2190 };
2191 self.microtasks.push_back(MicrotaskJob::Reaction {
2192 reaction,
2193 value,
2194 origin,
2195 });
2196 return Ok(derived);
2197 }
2198 self.charge_promise_reactions(2)?;
2199 let HeapEntry::Promise {
2200 state:
2201 PromiseState::Pending {
2202 fulfill_reactions,
2203 reject_reactions,
2204 },
2205 ..
2206 } = &mut self.heap[index]
2207 else {
2208 unreachable!("pending Promise state was checked before derived allocation");
2209 };
2210 fulfill_reactions.push(PromiseReaction::Fulfilled {
2211 handler: on_fulfilled,
2212 derived,
2213 });
2214 reject_reactions.push(PromiseReaction::Rejected {
2215 handler: on_rejected,
2216 derived,
2217 });
2218 Ok(derived)
2219 }
2220
2221 pub(crate) fn promise_finally(
2222 &mut self,
2223 promise: Value,
2224 handler: Value,
2225 ) -> Result<Value, EvalFailure> {
2226 let index = self
2227 .runtime_slot(promise)
2228 .map_err(EvalFailure::Runtime)?
2229 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2230 operation: "Promise.prototype.finally",
2231 }))?;
2232 let settled = match &self.heap[index] {
2233 HeapEntry::Promise {
2234 state: PromiseState::Pending { .. },
2235 ..
2236 } => None,
2237 HeapEntry::Promise {
2238 state: PromiseState::Fulfilled { value },
2239 ..
2240 } => Some((true, *value, ThrowOrigin::Bytecode)),
2241 HeapEntry::Promise {
2242 state: PromiseState::Rejected { reason, origin },
2243 ..
2244 } => Some((false, *reason, *origin)),
2245 _ => {
2246 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2247 operation: "Promise.prototype.finally",
2248 }));
2249 }
2250 };
2251 let derived = self.create_promise()?;
2252 let reaction = |completion| PromiseReaction::Finally {
2253 handler,
2254 derived,
2255 completion,
2256 };
2257 if let Some((fulfilled, value, origin)) = settled {
2258 self.ensure_microtask_capacity(1)
2259 .map_err(EvalFailure::Runtime)?;
2260 self.microtasks.push_back(MicrotaskJob::Reaction {
2261 reaction: reaction(if fulfilled {
2262 PromiseCompletion::Fulfilled
2263 } else {
2264 PromiseCompletion::Rejected
2265 }),
2266 value,
2267 origin,
2268 });
2269 return Ok(derived);
2270 }
2271 self.charge_promise_reactions(2)?;
2272 let HeapEntry::Promise {
2273 state:
2274 PromiseState::Pending {
2275 fulfill_reactions,
2276 reject_reactions,
2277 },
2278 ..
2279 } = &mut self.heap[index]
2280 else {
2281 unreachable!("pending Promise state was checked before derived allocation");
2282 };
2283 fulfill_reactions.push(reaction(PromiseCompletion::Fulfilled));
2284 reject_reactions.push(reaction(PromiseCompletion::Rejected));
2285 Ok(derived)
2286 }
2287
2288 pub(crate) fn create_promise_finally(
2289 &mut self,
2290 derived: Value,
2291 value: Value,
2292 origin: ThrowOrigin,
2293 completion: PromiseCompletion,
2294 ) -> Result<Value, EvalFailure> {
2295 self.allocate(HeapEntry::PromiseFinally {
2296 derived,
2297 value,
2298 origin,
2299 completion,
2300 })
2301 .map_err(EvalFailure::Runtime)
2302 }
2303
2304 pub(crate) fn fulfill_promise_finally(&mut self, record: Value) -> Result<(), EvalFailure> {
2305 let (derived, value, origin, completion) = self.promise_finally_record(record)?;
2306 match completion {
2307 PromiseCompletion::Fulfilled => self
2308 .resolve_promise(derived, value)
2309 .map_err(EvalFailure::Runtime),
2310 PromiseCompletion::Rejected => self
2311 .reject_promise(derived, value, origin)
2312 .map_err(EvalFailure::Runtime),
2313 }
2314 }
2315
2316 pub(crate) fn reject_promise_finally(
2317 &mut self,
2318 record: Value,
2319 reason: Value,
2320 ) -> Result<(), EvalFailure> {
2321 let (derived, _, _, _) = self.promise_finally_record(record)?;
2322 self.reject_promise(derived, reason, ThrowOrigin::Bytecode)
2323 .map_err(EvalFailure::Runtime)
2324 }
2325
2326 fn promise_finally_record(
2327 &mut self,
2328 record: Value,
2329 ) -> Result<(Value, Value, ThrowOrigin, PromiseCompletion), EvalFailure> {
2330 let index = self
2331 .runtime_slot(record)
2332 .map_err(EvalFailure::Runtime)?
2333 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2334 operation: "Promise finally target",
2335 }))?;
2336 let HeapEntry::PromiseFinally {
2337 derived,
2338 value,
2339 origin,
2340 completion,
2341 } = &self.heap[index]
2342 else {
2343 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2344 operation: "Promise finally target",
2345 }));
2346 };
2347 Ok((*derived, *value, *origin, *completion))
2348 }
2349
2350 pub(crate) fn promise_resolve(&mut self, value: Value) -> Result<Value, EvalFailure> {
2351 if matches!(self.runtime_slot(value).map_err(EvalFailure::Runtime)?, Some(index) if matches!(self.heap[index], HeapEntry::Promise { .. }))
2352 {
2353 return Ok(value);
2354 }
2355 let promise = self.create_promise()?;
2356 self.resolve_promise(promise, value)
2357 .map_err(EvalFailure::Runtime)?;
2358 Ok(promise)
2359 }
2360
2361 pub(crate) fn promise_reject(&mut self, reason: Value) -> Result<Value, EvalFailure> {
2362 let promise = self.create_promise()?;
2363 self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2364 .map_err(EvalFailure::Runtime)?;
2365 Ok(promise)
2366 }
2367
2368 pub(crate) fn promise_all(&mut self, iterable: Value) -> Result<Value, EvalFailure> {
2369 let promise = self.create_promise()?;
2370 let aggregate = self
2371 .allocate(HeapEntry::PromiseAll {
2372 promise,
2373 values: Vec::new(),
2374 remaining: 1,
2375 settled: false,
2376 })
2377 .map_err(EvalFailure::Runtime)?;
2378 let iterator = match self.create_iterator(iterable, IteratorKind::Sync) {
2379 Ok(iterator) => iterator,
2380 Err(failure) => {
2381 self.mark_promise_all_settled(aggregate)?;
2382 self.reject_promise_failure(promise, failure)?;
2383 return Ok(promise);
2384 }
2385 };
2386 loop {
2387 let value = match self.iterator_next(iterator) {
2388 Ok((true, _)) => break,
2389 Ok((false, value)) => value,
2390 Err(failure) => {
2391 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2392 }
2393 };
2394 let index = match self.add_promise_all_element(aggregate) {
2395 Ok(index) => index,
2396 Err(failure) => {
2397 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2398 }
2399 };
2400 let element = match self
2401 .allocate(HeapEntry::PromiseAllElement {
2402 aggregate,
2403 index,
2404 called: false,
2405 })
2406 .map_err(EvalFailure::Runtime)
2407 {
2408 Ok(element) => element,
2409 Err(failure) => {
2410 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2411 }
2412 };
2413 let (fulfill_target, reject_target) = self.intrinsics.builtins.promise_all_targets();
2414 let on_fulfilled = match self.create_promise_resolver_function(fulfill_target, element)
2415 {
2416 Ok(callback) => callback,
2417 Err(failure) => {
2418 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2419 }
2420 };
2421 let on_rejected = match self.create_promise_resolver_function(reject_target, element) {
2422 Ok(callback) => callback,
2423 Err(failure) => {
2424 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2425 }
2426 };
2427 let resolved = match self.promise_resolve(value) {
2428 Ok(resolved) => resolved,
2429 Err(failure) => {
2430 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2431 }
2432 };
2433 if let Err(failure) = self.promise_then(resolved, on_fulfilled, on_rejected) {
2434 return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2435 }
2436 }
2437 if let Some(values) = self.finish_promise_all(aggregate)? {
2438 let array = self.create_array(values)?;
2439 self.fulfill_promise(promise, array)
2440 .map_err(EvalFailure::Runtime)?;
2441 }
2442 Ok(promise)
2443 }
2444
2445 fn reject_promise_all_abrupt(
2446 &mut self,
2447 aggregate: Value,
2448 promise: Value,
2449 iterator: Value,
2450 failure: EvalFailure,
2451 ) -> Result<Value, EvalFailure> {
2452 self.mark_promise_all_settled(aggregate)?;
2453 if let Err(EvalFailure::Runtime(kind)) = self.close_iterator(iterator) {
2454 return Err(EvalFailure::Runtime(kind));
2455 }
2456 self.reject_promise_failure(promise, failure)?;
2457 Ok(promise)
2458 }
2459
2460 fn close_iterator(&mut self, iterator: Value) -> Result<(), EvalFailure> {
2461 let Some(index) = self.runtime_slot(iterator).map_err(EvalFailure::Runtime)? else {
2462 return Ok(());
2463 };
2464 let HeapEntry::Iterator {
2465 state: IteratorState::Protocol { iterator, .. },
2466 } = &self.heap[index]
2467 else {
2468 return Ok(());
2469 };
2470 let iterator = *iterator;
2471 let close = self.get_named_property(iterator, "return")?;
2472 if self.is_callable(close)? {
2473 let _ = self.call_value(close, iterator, &[])?;
2474 }
2475 Ok(())
2476 }
2477
2478 fn mark_promise_all_settled(&mut self, aggregate: Value) -> Result<bool, EvalFailure> {
2479 let index = self
2480 .runtime_slot(aggregate)
2481 .map_err(EvalFailure::Runtime)?
2482 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2483 operation: "Promise.all target",
2484 }))?;
2485 let HeapEntry::PromiseAll { settled, .. } = &mut self.heap[index] else {
2486 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2487 operation: "Promise.all target",
2488 }));
2489 };
2490 let changed = !*settled;
2491 *settled = true;
2492 Ok(changed)
2493 }
2494
2495 fn add_promise_all_element(&mut self, aggregate: Value) -> Result<usize, EvalFailure> {
2496 let index = self
2497 .runtime_slot(aggregate)
2498 .map_err(EvalFailure::Runtime)?
2499 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2500 operation: "Promise.all target",
2501 }))?;
2502 let next_remaining = match &self.heap[index] {
2503 HeapEntry::PromiseAll {
2504 remaining,
2505 settled: false,
2506 ..
2507 } => remaining.checked_add(1).ok_or(EvalFailure::Runtime(
2508 RuntimeErrorKind::HeapByteLimitExceeded {
2509 limit: self.limits.max_heap_bytes,
2510 },
2511 ))?,
2512 HeapEntry::PromiseAll { .. } => {
2513 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2514 operation: "Promise.all target",
2515 }));
2516 }
2517 _ => {
2518 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2519 operation: "Promise.all target",
2520 }));
2521 }
2522 };
2523 self.charge_heap(std::mem::size_of::<Value>())
2524 .map_err(EvalFailure::Runtime)?;
2525 let HeapEntry::PromiseAll {
2526 values, remaining, ..
2527 } = &mut self.heap[index]
2528 else {
2529 unreachable!("Promise.all aggregate was checked before its heap charge");
2530 };
2531 values.try_reserve(1).map_err(|_| {
2532 EvalFailure::Runtime(RuntimeErrorKind::HeapByteLimitExceeded {
2533 limit: self.limits.max_heap_bytes,
2534 })
2535 })?;
2536 let index = values.len();
2537 values.push(Value::UNDEFINED);
2538 *remaining = next_remaining;
2539 Ok(index)
2540 }
2541
2542 fn finish_promise_all(&mut self, aggregate: Value) -> Result<Option<Vec<Value>>, EvalFailure> {
2543 let index = self
2544 .runtime_slot(aggregate)
2545 .map_err(EvalFailure::Runtime)?
2546 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2547 operation: "Promise.all target",
2548 }))?;
2549 let HeapEntry::PromiseAll {
2550 values,
2551 remaining,
2552 settled,
2553 ..
2554 } = &mut self.heap[index]
2555 else {
2556 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2557 operation: "Promise.all target",
2558 }));
2559 };
2560 if *settled {
2561 return Ok(None);
2562 }
2563 *remaining -= 1;
2564 if *remaining != 0 {
2565 return Ok(None);
2566 }
2567 *settled = true;
2568 Ok(Some(std::mem::take(values)))
2569 }
2570
2571 pub(crate) fn resolve_promise_all_element(
2572 &mut self,
2573 element: Value,
2574 value: Value,
2575 ) -> Result<(), EvalFailure> {
2576 let index = self
2577 .runtime_slot(element)
2578 .map_err(EvalFailure::Runtime)?
2579 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2580 operation: "Promise.all target",
2581 }))?;
2582 let (aggregate, output_index) = {
2583 let HeapEntry::PromiseAllElement {
2584 aggregate,
2585 index: output_index,
2586 called,
2587 } = &mut self.heap[index]
2588 else {
2589 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2590 operation: "Promise.all target",
2591 }));
2592 };
2593 if *called {
2594 return Ok(());
2595 }
2596 *called = true;
2597 (*aggregate, *output_index)
2598 };
2599 let aggregate_index = self
2600 .runtime_slot(aggregate)
2601 .map_err(EvalFailure::Runtime)?
2602 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2603 operation: "Promise.all target",
2604 }))?;
2605 let (promise, values) = {
2606 let HeapEntry::PromiseAll {
2607 promise,
2608 values,
2609 remaining,
2610 settled,
2611 } = &mut self.heap[aggregate_index]
2612 else {
2613 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2614 operation: "Promise.all target",
2615 }));
2616 };
2617 if *settled {
2618 return Ok(());
2619 }
2620 values[output_index] = value;
2621 *remaining -= 1;
2622 let values = (*remaining == 0).then(|| {
2623 *settled = true;
2624 std::mem::take(values)
2625 });
2626 (*promise, values)
2627 };
2628 if let Some(values) = values {
2629 let array = self.create_array(values)?;
2630 self.fulfill_promise(promise, array)
2631 .map_err(EvalFailure::Runtime)?;
2632 }
2633 Ok(())
2634 }
2635
2636 pub(crate) fn reject_promise_all_element(
2637 &mut self,
2638 element: Value,
2639 reason: Value,
2640 ) -> Result<(), EvalFailure> {
2641 let index = self
2642 .runtime_slot(element)
2643 .map_err(EvalFailure::Runtime)?
2644 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2645 operation: "Promise.all target",
2646 }))?;
2647 let aggregate = {
2648 let HeapEntry::PromiseAllElement {
2649 aggregate, called, ..
2650 } = &mut self.heap[index]
2651 else {
2652 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2653 operation: "Promise.all target",
2654 }));
2655 };
2656 if *called {
2657 return Ok(());
2658 }
2659 *called = true;
2660 *aggregate
2661 };
2662 let aggregate_index = self
2663 .runtime_slot(aggregate)
2664 .map_err(EvalFailure::Runtime)?
2665 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2666 operation: "Promise.all target",
2667 }))?;
2668 let HeapEntry::PromiseAll { promise, .. } = &self.heap[aggregate_index] else {
2669 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2670 operation: "Promise.all target",
2671 }));
2672 };
2673 let promise = *promise;
2674 if !self.mark_promise_all_settled(aggregate)? {
2675 return Ok(());
2676 }
2677 self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2678 .map_err(EvalFailure::Runtime)
2679 }
2680
2681 fn create_array(&mut self, elements: Vec<Value>) -> Result<Value, EvalFailure> {
2682 self.allocate(HeapEntry::Array {
2683 elements,
2684 properties: PropertyMap::default(),
2685 prototype: Some(self.intrinsics.array_prototype),
2686 extensible: true,
2687 length_writable: true,
2688 })
2689 .map_err(EvalFailure::Runtime)
2690 }
2691
2692 fn resolve_promise(&mut self, promise: Value, value: Value) -> Result<(), RuntimeErrorKind> {
2693 if promise == value {
2694 return self
2695 .reject_promise_failure(
2696 promise,
2697 EvalFailure::Throw(ThrowOrigin::TypeError {
2698 operation: "Promise cannot resolve itself",
2699 }),
2700 )
2701 .map_err(|failure| match failure {
2702 EvalFailure::Runtime(kind) => kind,
2703 _ => RuntimeErrorKind::InvalidValue { value: promise },
2704 });
2705 }
2706 if !self.is_object(value) {
2707 return self.fulfill_promise(promise, value);
2708 }
2709 let then = match self.get_named_property(value, "then") {
2710 Ok(then) => then,
2711 Err(EvalFailure::Runtime(kind)) => return Err(kind),
2712 Err(failure) => {
2713 return self.reject_promise_failure(promise, failure).map_err(
2714 |failure| match failure {
2715 EvalFailure::Runtime(kind) => kind,
2716 _ => RuntimeErrorKind::InvalidValue { value: promise },
2717 },
2718 );
2719 }
2720 };
2721 if !self.is_callable(then).map_err(|failure| match failure {
2722 EvalFailure::Runtime(kind) => kind,
2723 _ => RuntimeErrorKind::InvalidValue { value: then },
2724 })? {
2725 return self.fulfill_promise(promise, value);
2726 }
2727 self.ensure_microtask_capacity(1)?;
2728 self.microtasks.push_back(MicrotaskJob::Thenable {
2729 promise,
2730 thenable: value,
2731 then,
2732 });
2733 Ok(())
2734 }
2735
2736 fn reject_promise(
2737 &mut self,
2738 promise: Value,
2739 reason: Value,
2740 origin: ThrowOrigin,
2741 ) -> Result<(), RuntimeErrorKind> {
2742 self.settle_promise(promise, PromiseState::Rejected { reason, origin })
2743 }
2744
2745 fn fulfill_promise(&mut self, promise: Value, value: Value) -> Result<(), RuntimeErrorKind> {
2746 self.settle_promise(promise, PromiseState::Fulfilled { value })
2747 }
2748
2749 fn settle_promise(
2750 &mut self,
2751 promise: Value,
2752 terminal: PromiseState,
2753 ) -> Result<(), RuntimeErrorKind> {
2754 let index = self
2755 .runtime_slot(promise)?
2756 .ok_or(RuntimeErrorKind::InvalidValue { value: promise })?;
2757 let reaction_count = match &self.heap[index] {
2758 HeapEntry::Promise {
2759 state:
2760 PromiseState::Pending {
2761 fulfill_reactions,
2762 reject_reactions,
2763 },
2764 ..
2765 } => match &terminal {
2766 PromiseState::Fulfilled { .. } => fulfill_reactions.len(),
2767 PromiseState::Rejected { .. } => reject_reactions.len(),
2768 PromiseState::Pending { .. } => unreachable!("Promise settlement is terminal"),
2769 },
2770 HeapEntry::Promise { .. } => return Ok(()),
2771 _ => return Err(RuntimeErrorKind::InvalidValue { value: promise }),
2772 };
2773 self.ensure_microtask_capacity(reaction_count)?;
2774 let reactions = match &mut self.heap[index] {
2775 HeapEntry::Promise { state, .. } => {
2776 let reactions = match state {
2777 PromiseState::Pending {
2778 fulfill_reactions,
2779 reject_reactions,
2780 } => match &terminal {
2781 PromiseState::Fulfilled { .. } => std::mem::take(fulfill_reactions),
2782 PromiseState::Rejected { .. } => std::mem::take(reject_reactions),
2783 PromiseState::Pending { .. } => {
2784 unreachable!("Promise settlement is terminal")
2785 }
2786 },
2787 _ => return Ok(()),
2788 };
2789 *state = terminal.clone();
2790 reactions
2791 }
2792 _ => return Err(RuntimeErrorKind::InvalidValue { value: promise }),
2793 };
2794 let (value, origin) = match terminal {
2795 PromiseState::Fulfilled { value } => (value, ThrowOrigin::Bytecode),
2796 PromiseState::Rejected { reason, origin } => (reason, origin),
2797 PromiseState::Pending { .. } => unreachable!("Promise settlement is terminal"),
2798 };
2799 for reaction in reactions {
2800 self.microtasks.push_back(MicrotaskJob::Reaction {
2801 reaction,
2802 value,
2803 origin,
2804 });
2805 }
2806 Ok(())
2807 }
2808
2809 fn reject_promise_failure(
2810 &mut self,
2811 promise: Value,
2812 failure: EvalFailure,
2813 ) -> Result<(), EvalFailure> {
2814 let (reason, origin) = self.promise_rejection_value(failure)?;
2815 self.reject_promise(promise, reason, origin)
2816 .map_err(EvalFailure::Runtime)
2817 }
2818
2819 fn promise_rejection_value(
2820 &mut self,
2821 failure: EvalFailure,
2822 ) -> Result<(Value, ThrowOrigin), EvalFailure> {
2823 match failure {
2824 EvalFailure::ThrowValue(value) => Ok((value, ThrowOrigin::Bytecode)),
2825 EvalFailure::ThrowValueOrigin { value, origin } => Ok((value, origin)),
2826 EvalFailure::Throw(ThrowOrigin::Bytecode) => {
2827 Ok((Value::UNDEFINED, ThrowOrigin::Bytecode))
2828 }
2829 EvalFailure::Throw(origin) => {
2830 let (name, message) = match origin {
2831 ThrowOrigin::TypeError { operation } => ("TypeError", operation),
2832 ThrowOrigin::RangeError { operation } => ("RangeError", operation),
2833 ThrowOrigin::ReferenceError { operation } => ("ReferenceError", operation),
2834 ThrowOrigin::UriError { operation } => ("URIError", operation),
2835 ThrowOrigin::Bytecode => unreachable!("handled above"),
2836 };
2837 let id = self
2838 .intrinsics
2839 .builtins
2840 .id_named(name)
2841 .expect("error constructor is installed");
2842 match self.throw_error(id, message.to_owned()) {
2843 EvalFailure::ThrowValue(value) => Ok((value, origin)),
2844 EvalFailure::Runtime(kind) => Err(EvalFailure::Runtime(kind)),
2845 _ => unreachable!("error materialization returns a thrown value"),
2846 }
2847 }
2848 EvalFailure::Runtime(kind) => Err(EvalFailure::Runtime(kind)),
2849 }
2850 }
2851
2852 fn program(&self) -> &Program<Verified> {
2853 self.program
2854 .expect("module registry operations require a whole program")
2855 }
2856
2857 fn module_code(&self, module: ModuleId) -> &Module<Verified> {
2858 let index = module.get() as usize;
2859 if index >= self.dynamic_base {
2860 return &self.dynamic[index - self.dynamic_base].program.modules()[0].code;
2861 }
2862 match self.program {
2863 Some(program) => {
2864 &program
2865 .module(module)
2866 .expect("verified module id remains in bounds")
2867 .code
2868 }
2869 None => self.module,
2870 }
2871 }
2872
2873 fn program_module(&self, module: ModuleId) -> &ProgramModule<Verified> {
2874 let index = module.get() as usize;
2875 if index >= self.dynamic_base {
2876 return &self.dynamic[index - self.dynamic_base].program.modules()[0];
2877 }
2878 self.program
2879 .and_then(|program| program.module(module))
2880 .expect("verified module id remains in bounds")
2881 }
2882
2883 fn validate_dynamic_script(program: &Program<Verified>) -> Result<(), &'static str> {
2885 if program.modules().len() != 1 {
2886 return Err("script program must contain exactly one module");
2887 }
2888 if program.entry() != ModuleId::new(0) {
2889 return Err("script program entry must be module zero");
2890 }
2891 let module = &program.modules()[0];
2892 if !module.edges.is_empty() || !module.bindings.is_empty() || !module.exports.is_empty() {
2893 return Err("script program must not contain linkage metadata");
2894 }
2895 if module
2896 .code
2897 .functions()
2898 .iter()
2899 .flat_map(|function| function.code())
2900 .any(|instruction| {
2901 matches!(
2902 instruction,
2903 Instruction::Import { .. } | Instruction::Export { .. }
2904 )
2905 })
2906 {
2907 return Err("script program must not contain import or export instructions");
2908 }
2909 Ok(())
2910 }
2911
2912 fn script_heap_cost(program: &Program<Verified>) -> usize {
2913 const MODULE_BYTES: usize = 64;
2914 const FUNCTION_BYTES: usize = 32;
2915 program.modules().iter().fold(0usize, |total, module| {
2916 let constant_bytes = module
2917 .code
2918 .constants()
2919 .iter()
2920 .fold(0usize, |bytes, constant| {
2921 let payload = match constant {
2922 Constant::String(text) => text.len_units().saturating_mul(2),
2923 Constant::BigInt(value) => value.as_str().len(),
2924 Constant::Number(_)
2925 | Constant::Int32(_)
2926 | Constant::Boolean(_)
2927 | Constant::Null
2928 | Constant::Undefined => 0,
2929 };
2930 bytes
2931 .saturating_add(std::mem::size_of::<Constant>())
2932 .saturating_add(payload)
2933 });
2934 let function_bytes =
2935 module
2936 .code
2937 .functions()
2938 .iter()
2939 .fold(0usize, |bytes, function| {
2940 bytes
2941 .saturating_add(FUNCTION_BYTES)
2942 .saturating_add(
2943 function
2944 .code()
2945 .len()
2946 .saturating_mul(std::mem::size_of::<Instruction>()),
2947 )
2948 .saturating_add(function.handlers().len().saturating_mul(
2949 std::mem::size_of::<bamts_bytecode::ExceptionHandler>(),
2950 ))
2951 });
2952 total
2953 .saturating_add(MODULE_BYTES)
2954 .saturating_add(constant_bytes)
2955 .saturating_add(function_bytes)
2956 .saturating_add(module.code.verification_bytes())
2957 })
2958 }
2959
2960 fn install_script_reserving(
2961 &mut self,
2962 program: Arc<Program<Verified>>,
2963 reserved_slots: usize,
2964 reserved_bytes: usize,
2965 ) -> Result<ModuleId, RuntimeErrorKind> {
2966 Self::validate_dynamic_script(&program)
2967 .map_err(|reason| RuntimeErrorKind::InvalidDynamicScript { reason })?;
2968 if self.dynamic.len() >= self.limits.max_dynamic_modules {
2969 return Err(RuntimeErrorKind::DynamicModuleLimitExceeded {
2970 limit: self.limits.max_dynamic_modules,
2971 });
2972 }
2973 let bytes = Self::script_heap_cost(&program);
2974 let retained_bytes =
2975 bytes
2976 .checked_add(reserved_bytes)
2977 .ok_or(RuntimeErrorKind::HeapByteLimitExceeded {
2978 limit: self.limits.max_heap_bytes,
2979 })?;
2980 self.ensure_allocation_capacity(reserved_slots, retained_bytes)?;
2981 self.charge_heap(bytes)?;
2982 let index = self.dynamic_base.checked_add(self.dynamic.len()).ok_or(
2983 RuntimeErrorKind::DynamicModuleLimitExceeded {
2984 limit: self.limits.max_dynamic_modules,
2985 },
2986 )?;
2987 let module = ModuleId::new(u32::try_from(index).map_err(|_| {
2988 RuntimeErrorKind::DynamicModuleLimitExceeded {
2989 limit: self.limits.max_dynamic_modules,
2990 }
2991 })?);
2992 self.dynamic.push(DynamicModule { program, bytes });
2993 self.registry.modules.push(ModuleInstance {
2994 binding_cells: Vec::new(),
2995 constant_cells: Vec::new(),
2996 namespace: None,
2997 state: ModuleState::Unevaluated,
2998 });
2999 debug_assert_eq!(
3000 self.dynamic
3001 .last()
3002 .expect("installed script remains retained")
3003 .bytes,
3004 bytes
3005 );
3006 debug_assert_eq!(
3007 self.registry.modules.len(),
3008 self.dynamic_base + self.dynamic.len()
3009 );
3010 Ok(module)
3011 }
3012
3013 fn allocate_cell(&mut self, value: Value, module: ModuleId) -> Result<CellId, RuntimeError> {
3014 if self.registry.cells.len() >= self.limits.max_module_cells {
3015 return Err(self.program_error(
3016 module,
3017 RuntimeErrorKind::ModuleCellLimitExceeded {
3018 limit: self.limits.max_module_cells,
3019 },
3020 ));
3021 }
3022 let id = CellId(self.registry.cells.len());
3023 self.registry.cells.push(Cell { value });
3024 Ok(id)
3025 }
3026
3027 pub(crate) fn instantiate_modules(&mut self) -> Result<(), RuntimeError> {
3028 debug_assert!(
3029 self.dynamic.is_empty(),
3030 "module instantiation precedes dynamic script installation"
3031 );
3032 let program = self
3033 .program
3034 .expect("module registry operations require a whole program");
3035 self.registry.modules = program
3036 .modules()
3037 .iter()
3038 .map(|module| ModuleInstance {
3039 binding_cells: vec![None; module.bindings.len()],
3040 constant_cells: vec![None; module.code.constants().len()],
3041 namespace: None,
3042 state: ModuleState::Unevaluated,
3043 })
3044 .collect();
3045
3046 for module_index in 0..program.modules().len() {
3047 let module_id = ModuleId::new(module_index as u32);
3048 let bindings = program.modules()[module_index].bindings.clone();
3049 for (binding_index, binding) in bindings.into_iter().enumerate() {
3050 let initial = match binding.kind {
3051 BindingKind::Hoisted => Some(Value::UNDEFINED),
3052 BindingKind::Lexical => Some(Value::UNINITIALIZED),
3053 BindingKind::Imported { .. } | BindingKind::Namespace { .. } => None,
3054 };
3055 if let Some(value) = initial {
3056 let cell = self.allocate_cell(value, module_id)?;
3057 self.registry.modules[module_index].binding_cells[binding_index] = Some(cell);
3058 }
3059 }
3060 }
3061
3062 for module_index in 0..program.modules().len() {
3063 let module_id = ModuleId::new(module_index as u32);
3064 let bindings = program.modules()[module_index].bindings.clone();
3065 for (binding_index, binding) in bindings.into_iter().enumerate() {
3066 let cell = match binding.kind {
3067 BindingKind::Hoisted | BindingKind::Lexical => continue,
3068 BindingKind::Imported { edge, name } => {
3069 let dependency = program.modules()[module_index].edges[edge.get() as usize];
3070 match dependency.target {
3071 EdgeTarget::External => {
3072 let name = self.constant_text(module_id, name).clone();
3073 self.external_export_cell(module_id, edge, &name)?
3074 }
3075 EdgeTarget::Local(target) => match program
3076 .resolve_export(target, self.constant_text(module_id, name))
3077 {
3078 Some(ResolvedExport::Local { module, binding }) => {
3079 self.registry.modules[module.get() as usize].binding_cells
3080 [binding.get() as usize]
3081 .expect("own cells are allocated before aliases link")
3082 }
3083 Some(ResolvedExport::External { module, edge, name }) => {
3084 let name = self.constant_text(module, name).clone();
3085 self.external_export_cell(module, edge, &name)?
3086 }
3087 None => {
3088 return Err(self.program_error(
3089 module_id,
3090 RuntimeErrorKind::InvalidVerifiedProgram {
3091 module: module_id,
3092 instruction: Instruction::Import {
3093 dst: bamts_bytecode::Register::new(0),
3094 specifier: name,
3095 },
3096 },
3097 ));
3098 }
3099 },
3100 }
3101 }
3102 BindingKind::Namespace { edge } => {
3103 let dependency = program.modules()[module_index].edges[edge.get() as usize];
3104 let namespace = match dependency.target {
3105 EdgeTarget::Local(target) => {
3106 self.module_namespace(target, module_id)?
3107 }
3108 EdgeTarget::External => self.external_namespace(module_id, edge)?,
3109 };
3110 self.allocate_cell(namespace, module_id)?
3111 }
3112 };
3113 self.registry.modules[module_index].binding_cells[binding_index] = Some(cell);
3114 }
3115 }
3116
3117 for module_index in 0..program.modules().len() {
3118 let bindings = &program.modules()[module_index].bindings;
3119 let constants = program.modules()[module_index].code.constants();
3120 for (constant_index, constant) in constants.iter().enumerate() {
3121 let Constant::String(name) = constant else {
3122 continue;
3123 };
3124 if let Some((binding_index, _)) =
3125 bindings.iter().enumerate().find(|(_, binding)| {
3126 self.constant_text(ModuleId::new(module_index as u32), binding.name) == name
3127 })
3128 {
3129 self.registry.modules[module_index].constant_cells[constant_index] =
3130 self.registry.modules[module_index].binding_cells[binding_index];
3131 }
3132 }
3133 }
3134 Ok(())
3135 }
3136
3137 fn module_namespace(
3138 &mut self,
3139 target: ModuleId,
3140 requester: ModuleId,
3141 ) -> Result<Value, RuntimeError> {
3142 if let Some(value) = self.registry.modules[target.get() as usize].namespace {
3143 return Ok(value);
3144 }
3145 let exported_names: Vec<EcmaString> = self
3146 .program_module(target)
3147 .exports
3148 .iter()
3149 .map(|export| self.constant_text(target, export.name).clone())
3150 .collect();
3151 for exported_name in exported_names {
3152 if let Some(ResolvedExport::External { module, edge, name }) =
3153 self.program().resolve_export(target, &exported_name)
3154 {
3155 let name = self.constant_text(module, name).clone();
3156 self.external_export_cell(module, edge, &name)?;
3157 }
3158 }
3159 let value = self
3160 .allocate(HeapEntry::ModuleNamespace { module: target })
3161 .map_err(|kind| self.program_error(requester, kind))?;
3162 self.registry.modules[target.get() as usize].namespace = Some(value);
3163 Ok(value)
3164 }
3165
3166 fn external_specifier(&self, module: ModuleId, edge: EdgeId) -> Option<EcmaString> {
3167 let dependency = self.program_module(module).edges[edge.get() as usize];
3168 let specifier = self.constant_text(module, dependency.specifier);
3169 self.registry
3170 .external
3171 .contains_key(specifier)
3172 .then(|| specifier.clone())
3173 }
3174
3175 fn external_namespace(
3176 &mut self,
3177 module: ModuleId,
3178 edge: EdgeId,
3179 ) -> Result<Value, RuntimeError> {
3180 let Some(specifier) = self.external_specifier(module, edge) else {
3181 return Err(self.program_error(
3182 module,
3183 RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3184 ));
3185 };
3186 let export_names: Vec<EcmaString> = self.registry.external[&specifier]
3187 .exports
3188 .keys()
3189 .cloned()
3190 .collect();
3191 for name in export_names {
3192 self.external_export_cell(module, edge, &name)?;
3193 }
3194 Ok(self.registry.external[&specifier].namespace)
3195 }
3196
3197 fn external_export_cell(
3198 &mut self,
3199 module: ModuleId,
3200 edge: EdgeId,
3201 name: &EcmaString,
3202 ) -> Result<CellId, RuntimeError> {
3203 let Some(specifier) = self.external_specifier(module, edge) else {
3204 return Err(self.program_error(
3205 module,
3206 RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3207 ));
3208 };
3209 let Some(export) = self.registry.external[&specifier]
3210 .exports
3211 .get(name)
3212 .copied()
3213 else {
3214 return Err(self.program_error(
3215 module,
3216 RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3217 ));
3218 };
3219 if let Some(cell) = export.cell {
3220 return Ok(cell);
3221 }
3222 let cell = self.allocate_cell(export.value, module)?;
3223 self.registry
3224 .external
3225 .get_mut(&specifier)
3226 .expect("external module remains registered")
3227 .exports
3228 .get_mut(name)
3229 .expect("external export remains registered")
3230 .cell = Some(cell);
3231 Ok(cell)
3232 }
3233
3234 pub(crate) fn resolve_import(
3235 &self,
3236 module: ModuleId,
3237 specifier: ConstantId,
3238 ) -> Result<ImportTarget, RuntimeErrorKind> {
3239 let name = self.constant_text(module, specifier);
3240 self.program_module(module)
3241 .edges
3242 .iter()
3243 .enumerate()
3244 .find(|(_, edge)| {
3245 edge.kind.has_dynamic() && self.constant_text(module, edge.specifier) == name
3246 })
3247 .map(|(index, edge)| match edge.target {
3248 EdgeTarget::Local(target) => ImportTarget::Local(target),
3249 EdgeTarget::External => ImportTarget::External(EdgeId::new(index as u32)),
3250 })
3251 .ok_or(RuntimeErrorKind::DynamicImportEdgeMissing { module, specifier })
3252 }
3253
3254 pub(crate) fn imported_namespace(
3255 &mut self,
3256 requester: ModuleId,
3257 target: ImportTarget,
3258 ) -> Result<Value, RuntimeErrorKind> {
3259 match target {
3260 ImportTarget::Local(target) => self.module_namespace(target, requester),
3261 ImportTarget::External(edge) => self.external_namespace(requester, edge),
3262 }
3263 .map_err(|error| error.kind)
3264 }
3265
3266 fn run_import_entry(&mut self, module: ModuleId) -> Result<(), RuntimeError> {
3267 let function = self.module_code(module).entry();
3268 let stop_depth = self.frames.len();
3269 self.push_frame(
3270 RuntimeFunction { module, function },
3271 &[],
3272 Value::UNDEFINED,
3273 Value::UNDEFINED,
3274 &[],
3275 None,
3276 )?;
3277 let result = self.run_loop(stop_depth).and_then(|execution| {
3278 execution.map(|_| ()).ok_or_else(|| {
3279 self.program_error(
3280 module,
3281 RuntimeErrorKind::InvalidVerifiedProgram {
3282 module,
3283 instruction: Instruction::Halt,
3284 },
3285 )
3286 })
3287 });
3288 if result.is_err() {
3289 self.unwind_frames_to(stop_depth);
3290 }
3291 result
3292 }
3293
3294 fn evaluate_import(&mut self, module: ModuleId) -> Result<(), RuntimeError> {
3295 let dependencies = match self.begin_module_evaluation(module)? {
3296 ModuleEvaluation::Cycle => return Ok(()),
3297 ModuleEvaluation::Evaluated(result) => return result,
3298 ModuleEvaluation::Ready(dependencies) => dependencies,
3299 };
3300 for dependency in dependencies {
3301 if let Err(error) = self.evaluate_import(dependency) {
3302 self.settle_module_evaluation(module, Err(error.clone()));
3303 return Err(error);
3304 }
3305 }
3306 let result = self.run_import_entry(module);
3307 self.settle_module_evaluation(module, result.clone());
3308 result
3309 }
3310
3311 fn import_namespace(
3312 &mut self,
3313 requester: ModuleId,
3314 specifier: ConstantId,
3315 ) -> Result<Value, EvalFailure> {
3316 let target = self
3317 .resolve_import(requester, specifier)
3318 .map_err(EvalFailure::Runtime)?;
3319 if let ImportTarget::Local(module) = target {
3320 self.evaluate_import(module)
3321 .map_err(|error| import_failure(&error))?;
3322 }
3323 self.imported_namespace(requester, target)
3324 .map_err(EvalFailure::Runtime)
3325 }
3326 fn evaluate_module(&mut self, module: ModuleId) -> Result<Option<Execution>, RuntimeError> {
3327 let dependencies = match self.begin_module_evaluation(module)? {
3328 ModuleEvaluation::Cycle => return Ok(None),
3329 ModuleEvaluation::Evaluated(result) => return result.map(|()| None),
3330 ModuleEvaluation::Ready(dependencies) => dependencies,
3331 };
3332 for dependency in dependencies {
3333 if let Err(error) = self.evaluate_module(dependency) {
3334 return self.finish_module_evaluation(module, Err(error)).map(Some);
3335 }
3336 }
3337
3338 let code = self.module_code(module);
3339 let function = code.entry().get() as usize;
3340 let metadata = &code.functions()[function];
3341 let register_count = metadata.register_count() as usize;
3342 let result = if self.limits.max_call_depth < 1 {
3343 Err(self.program_error(
3344 module,
3345 RuntimeErrorKind::CallDepthExceeded {
3346 limit: self.limits.max_call_depth,
3347 },
3348 ))
3349 } else if register_count > self.limits.max_total_registers {
3350 Err(self.program_error(
3351 module,
3352 RuntimeErrorKind::RegisterLimitExceeded {
3353 limit: self.limits.max_total_registers,
3354 },
3355 ))
3356 } else {
3357 self.frames.push(Frame::new(
3358 RuntimeFunction {
3359 module,
3360 function: FunctionId::new(function as u32),
3361 },
3362 metadata,
3363 &[],
3364 Value::UNDEFINED,
3365 Value::UNDEFINED,
3366 &[],
3367 None,
3368 ));
3369 self.live_registers = register_count;
3370 self.run_loop(0).and_then(|execution| {
3371 execution.ok_or_else(|| {
3372 self.program_error(
3373 module,
3374 RuntimeErrorKind::InvalidVerifiedProgram {
3375 module,
3376 instruction: Instruction::Halt,
3377 },
3378 )
3379 })
3380 })
3381 };
3382 self.finish_module_evaluation(module, result).map(Some)
3383 }
3384
3385 pub(crate) fn begin_module_evaluation(
3386 &mut self,
3387 module: ModuleId,
3388 ) -> Result<ModuleEvaluation, RuntimeError> {
3389 match self.registry.modules[module.get() as usize].state.clone() {
3390 ModuleState::Evaluating => return Ok(ModuleEvaluation::Cycle),
3391 ModuleState::Evaluated(result) => return Ok(ModuleEvaluation::Evaluated(result)),
3392 ModuleState::Unevaluated => {}
3393 }
3394 self.registry.modules[module.get() as usize].state = ModuleState::Evaluating;
3395
3396 let mut dependencies = Vec::new();
3397 for (edge_index, edge) in self
3398 .program_module(module)
3399 .edges
3400 .iter()
3401 .copied()
3402 .enumerate()
3403 {
3404 if !edge.kind.has_static() {
3405 continue;
3406 }
3407 match edge.target {
3408 EdgeTarget::Local(dependency) => dependencies.push(dependency),
3409 EdgeTarget::External
3410 if self
3411 .external_specifier(module, EdgeId::new(edge_index as u32))
3412 .is_some() => {}
3413 EdgeTarget::External => {
3414 let error = self.program_error(
3415 module,
3416 RuntimeErrorKind::ExternalModuleUnavailable {
3417 module,
3418 edge: EdgeId::new(edge_index as u32),
3419 },
3420 );
3421 self.settle_module_evaluation(module, Err(error.clone()));
3422 return Err(error);
3423 }
3424 }
3425 }
3426 Ok(ModuleEvaluation::Ready(dependencies))
3427 }
3428
3429 pub(crate) fn finish_module_evaluation(
3430 &mut self,
3431 module: ModuleId,
3432 result: Result<Execution, RuntimeError>,
3433 ) -> Result<Execution, RuntimeError> {
3434 if result.is_err() {
3435 self.frames.clear();
3436 self.live_registers = 0;
3437 }
3438 let stored = result.as_ref().map(|_| ()).map_err(Clone::clone);
3439 self.settle_module_evaluation(module, stored);
3440 result
3441 }
3442
3443 pub(crate) fn settle_module_evaluation(
3444 &mut self,
3445 module: ModuleId,
3446 result: Result<(), RuntimeError>,
3447 ) {
3448 match result {
3449 Ok(()) => {
3450 self.registry.modules[module.get() as usize].state = ModuleState::Evaluated(Ok(()));
3451 }
3452 Err(error) if matches!(error.kind, RuntimeErrorKind::UncaughtThrow { .. }) => {
3453 self.registry.modules[module.get() as usize].state =
3454 ModuleState::Evaluated(Err(error));
3455 }
3456 Err(_) => self.abort_module_evaluation(module),
3457 }
3458 }
3459
3460 pub(crate) fn abort_module_evaluation(&mut self, module: ModuleId) {
3461 if matches!(
3462 self.registry.modules[module.get() as usize].state,
3463 ModuleState::Evaluating
3464 ) {
3465 self.registry.modules[module.get() as usize].state = ModuleState::Unevaluated;
3466 }
3467 }
3468
3469 pub(crate) fn constant_text(&self, module: ModuleId, id: ConstantId) -> &EcmaString {
3470 match &self.module_code(module).constants()[id.get() as usize] {
3471 Constant::String(text) => text,
3472 _ => unreachable!("verified module names are strings"),
3473 }
3474 }
3475
3476 fn program_error(&self, module: ModuleId, kind: RuntimeErrorKind) -> RuntimeError {
3477 let code = self.module_code(module);
3478 let function = code.entry().get() as usize;
3479 let instruction = code.functions()[function]
3480 .code()
3481 .first()
3482 .copied()
3483 .unwrap_or(Instruction::Halt);
3484 RuntimeError {
3485 kind,
3486 function: FunctionId::new(function as u32),
3487 pc: Pc::new(0),
3488 source: RuntimeSource {
3489 function_name: None,
3490 instruction,
3491 },
3492 }
3493 }
3494
3495 fn run_loop(&mut self, stop_depth: usize) -> Result<Option<Execution>, RuntimeError> {
3496 if self.frames.len().saturating_add(self.native_depth) > self.limits.max_call_depth {
3497 return Err(self.error_here(RuntimeErrorKind::CallDepthExceeded {
3498 limit: self.limits.max_call_depth,
3499 }));
3500 }
3501 if self.live_registers > self.limits.max_total_registers {
3502 return Err(self.error_here(RuntimeErrorKind::RegisterLimitExceeded {
3503 limit: self.limits.max_total_registers,
3504 }));
3505 }
3506
3507 loop {
3508 let frame_index = self.frames.len() - 1;
3509 let (module_id, function_index, pc) = {
3510 let frame = &self.frames[frame_index];
3511 (frame.module, frame.function, frame.pc)
3512 };
3513 if let Err(kind) = self.consume_fuel(1) {
3514 return Err(self.error_at(kind, function_index, pc));
3515 }
3516 let instruction = self.module_code(module_id).functions()[function_index].code()[pc];
3517
3518 match instruction {
3519 Instruction::LoadConst { dst, constant } => {
3520 let value = self.load_constant(constant, function_index, pc)?;
3521 self.write_register(frame_index, dst.get(), value);
3522 self.frames[frame_index].pc = pc + 1;
3523 }
3524 Instruction::Move { dst, src } => {
3525 let value = self.read_register(frame_index, src.get());
3526 self.write_register(frame_index, dst.get(), value);
3527 self.frames[frame_index].pc = pc + 1;
3528 }
3529 Instruction::Unary { dst, op, operand } => {
3530 let value = self.read_register(frame_index, operand.get());
3531 match self.eval_unary(op, value) {
3532 Ok(result) => {
3533 self.write_register(frame_index, dst.get(), result);
3534 self.frames[frame_index].pc = pc + 1;
3535 }
3536 Err(failure) => self.resolve_failure(failure, pc)?,
3537 }
3538 }
3539 Instruction::Binary {
3540 dst,
3541 op,
3542 left,
3543 right,
3544 } => {
3545 let left = self.read_register(frame_index, left.get());
3546 let right = self.read_register(frame_index, right.get());
3547 match self.eval_binary(op, left, right) {
3548 Ok(result) => {
3549 self.write_register(frame_index, dst.get(), result);
3550 self.frames[frame_index].pc = pc + 1;
3551 }
3552 Err(failure) => self.resolve_failure(failure, pc)?,
3553 }
3554 }
3555 Instruction::CreateObject { dst } => {
3556 let value = self
3557 .allocate(HeapEntry::Object {
3558 properties: PropertyMap::default(),
3559 prototype: Some(self.intrinsics.object_prototype),
3560 boxed_primitive: None,
3561 extensible: true,
3562 })
3563 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3564 self.write_register(frame_index, dst.get(), value);
3565 self.frames[frame_index].pc = pc + 1;
3566 }
3567 Instruction::CreateArray { dst } => {
3568 let value = self
3569 .allocate(HeapEntry::Array {
3570 elements: Vec::new(),
3571 properties: PropertyMap::default(),
3572 prototype: Some(self.intrinsics.array_prototype),
3573 extensible: true,
3574 length_writable: true,
3575 })
3576 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3577 self.write_register(frame_index, dst.get(), value);
3578 self.frames[frame_index].pc = pc + 1;
3579 }
3580 Instruction::CreateCell { dst } => {
3581 let value = self
3582 .allocate(HeapEntry::Array {
3583 elements: vec![Value::UNINITIALIZED],
3584 properties: PropertyMap::default(),
3585 prototype: Some(self.intrinsics.array_prototype),
3586 extensible: true,
3587 length_writable: true,
3588 })
3589 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3590 self.write_register(frame_index, dst.get(), value);
3591 self.frames[frame_index].pc = pc + 1;
3592 }
3593 Instruction::CreateClosure {
3594 dst,
3595 function,
3596 captures,
3597 } => match self.read_captures(frame_index, captures.get(), function) {
3598 Ok(captures) => {
3599 let value = self
3600 .allocate(HeapEntry::Function {
3601 module: module_id,
3602 function,
3603 captures,
3604 properties: PropertyMap::default(),
3605 prototype: Some(self.intrinsics.function_prototype),
3606 extensible: true,
3607 })
3608 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3609 self.write_register(frame_index, dst.get(), value);
3610 self.frames[frame_index].pc = pc + 1;
3611 }
3612 Err(failure) => self.resolve_failure(failure, pc)?,
3613 },
3614 Instruction::GetProperty { dst, object, key } => {
3615 let object = self.read_register(frame_index, object.get());
3616 let key_value = self.read_register(frame_index, key.get());
3617 let key = match self.to_property_key(key_value) {
3618 Ok(key) => key,
3619 Err(failure) => {
3620 self.resolve_failure(failure, pc)?;
3621 continue;
3622 }
3623 };
3624 match self.resolve_get(object, &key) {
3625 Ok(GetOutcome::Value(value)) => {
3626 self.write_register(frame_index, dst.get(), value);
3627 self.frames[frame_index].pc = pc + 1;
3628 }
3629 Ok(GetOutcome::Text(text)) => {
3630 let value = self
3631 .allocate(HeapEntry::String(text))
3632 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3633 self.write_register(frame_index, dst.get(), value);
3634 self.frames[frame_index].pc = pc + 1;
3635 }
3636 Ok(GetOutcome::Getter(getter)) => {
3637 self.frames[frame_index].pc = pc + 1;
3638 self.execute_call(CallRequest {
3639 callee: getter,
3640 this_value: object,
3641 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::SetProperty { object, key, value } => {
3652 let object = self.read_register(frame_index, object.get());
3653 let value = self.read_register(frame_index, value.get());
3654 let key_value = self.read_register(frame_index, key.get());
3655 let key = match self.to_property_key(key_value) {
3656 Ok(key) => key,
3657 Err(failure) => {
3658 self.resolve_failure(failure, pc)?;
3659 continue;
3660 }
3661 };
3662 match self.resolve_set(object, key, value) {
3663 Ok(SetOutcome::Done) => self.frames[frame_index].pc = pc + 1,
3664 Ok(SetOutcome::Setter(setter)) => {
3665 self.frames[frame_index].pc = pc + 1;
3666 self.execute_call(CallRequest {
3667 callee: setter,
3668 this_value: object,
3669 arguments: &[value],
3670 destination: None,
3671 call_pc: pc,
3672 constructed: None,
3673 new_target: Value::UNDEFINED,
3674 })?;
3675 }
3676 Err(failure) => self.resolve_failure(failure, pc)?,
3677 }
3678 }
3679 Instruction::DeleteProperty { dst, object, key } => {
3680 let object = self.read_register(frame_index, object.get());
3681 let key_value = self.read_register(frame_index, key.get());
3682 let key = match self.to_property_key(key_value) {
3683 Ok(key) => key,
3684 Err(failure) => {
3685 self.resolve_failure(failure, pc)?;
3686 continue;
3687 }
3688 };
3689 match self.delete_property(object, &key) {
3690 Ok(deleted) => {
3691 self.write_register(frame_index, dst.get(), Value::boolean(deleted));
3692 self.frames[frame_index].pc = pc + 1;
3693 }
3694 Err(failure) => self.resolve_failure(failure, pc)?,
3695 }
3696 }
3697 Instruction::DefineAccessor {
3698 object,
3699 key,
3700 accessor,
3701 kind,
3702 } => {
3703 let object = self.read_register(frame_index, object.get());
3704 let accessor = self.read_register(frame_index, accessor.get());
3705 let key_value = self.read_register(frame_index, key.get());
3706 let key = match self.to_property_key(key_value) {
3707 Ok(key) => key,
3708 Err(failure) => {
3709 self.resolve_failure(failure, pc)?;
3710 continue;
3711 }
3712 };
3713 match self.define_accessor(object, key, accessor, kind) {
3714 Ok(()) => self.frames[frame_index].pc = pc + 1,
3715 Err(failure) => self.resolve_failure(failure, pc)?,
3716 }
3717 }
3718 Instruction::Call {
3719 dst,
3720 callee,
3721 this_value,
3722 arguments,
3723 } => {
3724 let callee = self.read_register(frame_index, callee.get());
3725 let this_value = self.read_register(frame_index, this_value.get());
3726 match self.read_arguments(frame_index, arguments.get()) {
3727 Ok(arguments) => {
3728 self.frames[frame_index].pc = pc + 1;
3729 self.execute_call(CallRequest {
3730 callee,
3731 this_value,
3732 arguments: &arguments,
3733 destination: Some(dst.get()),
3734 call_pc: pc,
3735 constructed: None,
3736 new_target: Value::UNDEFINED,
3737 })?;
3738 }
3739 Err(failure) => self.resolve_failure(failure, pc)?,
3740 }
3741 }
3742 Instruction::Construct {
3743 dst,
3744 callee,
3745 arguments,
3746 } => {
3747 let callee = self.read_register(frame_index, callee.get());
3748 match self.read_arguments(frame_index, arguments.get()) {
3749 Ok(arguments) => {
3750 self.frames[frame_index].pc = pc + 1;
3751 self.execute_construct(callee, &arguments, dst.get(), pc)?;
3752 }
3753 Err(failure) => self.resolve_failure(failure, pc)?,
3754 }
3755 }
3756 Instruction::LoadGlobal { dst, name } => match self.load_global(module_id, name) {
3757 Ok(Some(value)) => {
3758 self.write_register(frame_index, dst.get(), value);
3759 self.frames[frame_index].pc = pc + 1;
3760 }
3761 Ok(None) => self.throw(
3762 Value::UNDEFINED,
3763 ThrowOrigin::ReferenceError {
3764 operation: "global is not defined",
3765 },
3766 pc,
3767 )?,
3768 Err(kind) => return Err(self.error_here_at(kind, pc)),
3769 },
3770 Instruction::StoreGlobal { name, value } => {
3771 let value = self.read_register(frame_index, value.get());
3772 match self.store_global(module_id, name, value) {
3773 Ok(()) => self.frames[frame_index].pc = pc + 1,
3774 Err(failure) => self.resolve_failure(failure, pc)?,
3775 }
3776 }
3777 Instruction::TypeOfGlobal { dst, name } => {
3778 let text = match self.load_global(module_id, name) {
3779 Ok(value) => value.map_or("undefined", |value| self.type_of(value)),
3780 Err(kind) => return Err(self.error_here_at(kind, pc)),
3781 };
3782 let value = self
3783 .allocate(HeapEntry::String(EcmaString::from_utf8(text)))
3784 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3785 self.write_register(frame_index, dst.get(), value);
3786 self.frames[frame_index].pc = pc + 1;
3787 }
3788 Instruction::LoadThis { dst } => {
3789 let value = self.frames[frame_index].this_value;
3790 self.write_register(frame_index, dst.get(), value);
3791 self.frames[frame_index].pc = pc + 1;
3792 }
3793 Instruction::LoadArguments { dst } => {
3794 let value = self.materialize_arguments(frame_index, function_index, pc)?;
3795 self.write_register(frame_index, dst.get(), value);
3796 self.frames[frame_index].pc = pc + 1;
3797 }
3798 Instruction::LoadNewTarget { dst } => {
3799 let value = self.frames[frame_index].new_target;
3800 self.write_register(frame_index, dst.get(), value);
3801 self.frames[frame_index].pc = pc + 1;
3802 }
3803 Instruction::ArrayPush { array, value } => {
3804 let array = self.read_register(frame_index, array.get());
3805 let value = self.read_register(frame_index, value.get());
3806 match self.array_push(array, value) {
3807 Ok(()) => self.frames[frame_index].pc = pc + 1,
3808 Err(failure) => self.resolve_failure(failure, pc)?,
3809 }
3810 }
3811 Instruction::ArrayExtend { array, iterable } => {
3812 let array = self.read_register(frame_index, array.get());
3813 let iterable = self.read_register(frame_index, iterable.get());
3814 match self.array_extend(array, iterable) {
3815 Ok(()) => self.frames[frame_index].pc = pc + 1,
3816 Err(failure) => self.resolve_failure(failure, pc)?,
3817 }
3818 }
3819 Instruction::ObjectSpread { target, source } => {
3820 let target = self.read_register(frame_index, target.get());
3821 let source = self.read_register(frame_index, source.get());
3822 match self.object_spread(target, source) {
3823 Ok(()) => self.frames[frame_index].pc = pc + 1,
3824 Err(failure) => self.resolve_failure(failure, pc)?,
3825 }
3826 }
3827 Instruction::SetPrototype { object, prototype } => {
3828 let object = self.read_register(frame_index, object.get());
3829 let prototype = self.read_register(frame_index, prototype.get());
3830 match self.set_prototype(object, prototype) {
3831 Ok(()) => self.frames[frame_index].pc = pc + 1,
3832 Err(failure) => self.resolve_failure(failure, pc)?,
3833 }
3834 }
3835 Instruction::CreatePrivateName { dst, description } => {
3836 let description = self.constant_string(description).clone();
3837 let value = self
3838 .allocate(HeapEntry::PrivateName { description })
3839 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3840 self.write_register(frame_index, dst.get(), value);
3841 self.frames[frame_index].pc = pc + 1;
3842 }
3843 Instruction::CreateRegExp {
3844 dst,
3845 pattern,
3846 flags,
3847 } => {
3848 let pattern = self.constant_string(pattern).clone();
3849 let flags = self.constant_string(flags).clone();
3850 let value = self
3851 .allocate(HeapEntry::RegExp {
3852 pattern,
3853 flags,
3854 properties: PropertyMap::default(),
3855 prototype: Some(self.intrinsics.regexp_prototype()),
3856 extensible: true,
3857 })
3858 .map_err(|kind| self.error_at(kind, function_index, pc))?;
3859 self.write_register(frame_index, dst.get(), value);
3860 self.frames[frame_index].pc = pc + 1;
3861 }
3862 Instruction::GetIterator { dst, src, kind } => {
3863 let src = self.read_register(frame_index, src.get());
3864 match self.create_iterator(src, kind) {
3865 Ok(value) => {
3866 self.write_register(frame_index, dst.get(), value);
3867 self.frames[frame_index].pc = pc + 1;
3868 }
3869 Err(failure) => self.resolve_failure(failure, pc)?,
3870 }
3871 }
3872 Instruction::IteratorNext {
3873 done,
3874 value,
3875 iterator,
3876 } => {
3877 let iterator = self.read_register(frame_index, iterator.get());
3878 match self.iterator_next(iterator) {
3879 Ok((is_done, produced)) => {
3880 self.write_register(frame_index, done.get(), Value::boolean(is_done));
3881 self.write_register(frame_index, value.get(), produced);
3882 self.frames[frame_index].pc = pc + 1;
3883 }
3884 Err(failure) => self.resolve_failure(failure, pc)?,
3885 }
3886 }
3887 Instruction::Jump { target } => {
3888 self.frames[frame_index].pc = target.get() as usize;
3889 }
3890 Instruction::JumpIfTrue { condition, target } => {
3891 let condition = self.read_register(frame_index, condition.get());
3892 self.frames[frame_index].pc = if self.truthy(condition) {
3893 target.get() as usize
3894 } else {
3895 pc + 1
3896 };
3897 }
3898 Instruction::JumpIfFalse { condition, target } => {
3899 let condition = self.read_register(frame_index, condition.get());
3900 self.frames[frame_index].pc = if self.truthy(condition) {
3901 pc + 1
3902 } else {
3903 target.get() as usize
3904 };
3905 }
3906 Instruction::Return { value } => {
3907 let value = self.read_register(frame_index, value.get());
3908 if let Some(execution) = self.complete_frame(value) {
3909 return Ok(Some(execution));
3910 }
3911 if self.frames.len() == stop_depth {
3912 return Ok(None);
3913 }
3914 }
3915 Instruction::Throw { value } => {
3916 let value = self.read_register(frame_index, value.get());
3917 self.throw(value, ThrowOrigin::Bytecode, pc)?;
3918 }
3919 Instruction::Suspend { src, .. }
3920 if self
3921 .async_boundaries
3922 .last()
3923 .is_some_and(|boundary| *boundary == frame_index) =>
3924 {
3925 let awaited = self.read_register(frame_index, src.get());
3926 let frame = self.frames.pop().expect("async activation is executing");
3927 self.pending_async_suspend = Some((
3928 awaited,
3929 SuspendedActivation {
3930 target: RuntimeFunction {
3931 module: frame.module,
3932 function: FunctionId::new(frame.function as u32),
3933 },
3934 registers: frame.registers,
3935 this_value: frame.this_value,
3936 new_target: frame.new_target,
3937 args: frame.args,
3938 arguments_object: frame.arguments_object,
3939 resume_token: pc as u32 + 1,
3940 },
3941 ));
3942 return Ok(None);
3943 }
3944 Instruction::Suspend { src, .. }
3945 if self
3946 .generator_boundaries
3947 .last()
3948 .is_some_and(|boundary| *boundary == frame_index) =>
3949 {
3950 let value = self.read_register(frame_index, src.get());
3951 let frame = self
3952 .frames
3953 .pop()
3954 .expect("generator activation is executing");
3955 self.pending_generator_resume = Some(GeneratorResume::Yield {
3956 value,
3957 activation: SuspendedActivation {
3958 target: RuntimeFunction {
3959 module: frame.module,
3960 function: FunctionId::new(frame.function as u32),
3961 },
3962 registers: frame.registers,
3963 this_value: frame.this_value,
3964 new_target: frame.new_target,
3965 args: frame.args,
3966 arguments_object: frame.arguments_object,
3967 resume_token: pc as u32 + 1,
3968 },
3969 });
3970 return Ok(None);
3971 }
3972 Instruction::Suspend { .. } => {
3973 self.throw_type("suspend outside an engine-owned event loop", pc)?;
3974 }
3975 Instruction::Import { dst, specifier } => {
3976 match self.import_namespace(module_id, specifier) {
3977 Ok(namespace) => {
3978 self.write_register(frame_index, dst.get(), namespace);
3979 self.frames[frame_index].pc = pc + 1;
3980 }
3981 Err(failure) => self.resolve_failure(failure, pc)?,
3982 }
3983 }
3984 Instruction::Export { .. } => {
3985 return Err(self.error_here_at(
3986 RuntimeErrorKind::InvalidVerifiedProgram {
3987 module: module_id,
3988 instruction,
3989 },
3990 pc,
3991 ));
3992 }
3993 Instruction::Halt => {
3994 if let Some(execution) = self.complete_frame(Value::UNDEFINED) {
3995 return Ok(Some(execution));
3996 }
3997 if self.frames.len() == stop_depth {
3998 return Ok(None);
3999 }
4000 }
4001 }
4002 }
4003 }
4004
4005 fn read_register(&self, frame: usize, register: u32) -> Value {
4006 self.frames[frame].registers[register as usize]
4007 }
4008
4009 fn write_register(&mut self, frame: usize, register: u32, value: Value) {
4010 self.frames[frame].registers[register as usize] = value;
4011 }
4012
4013 fn constant_string(&self, id: ConstantId) -> &EcmaString {
4014 self.constant_text(self.active_module_id(), id)
4015 }
4016
4017 fn load_constant(
4018 &mut self,
4019 id: ConstantId,
4020 function: usize,
4021 pc: usize,
4022 ) -> Result<Value, RuntimeError> {
4023 self.load_constant_value(self.active_module_id(), id)
4024 .map_err(|kind| self.error_at(kind, function, pc))
4025 }
4026
4027 fn allocate(&mut self, entry: HeapEntry) -> Result<Value, RuntimeErrorKind> {
4028 let bytes = entry.initial_bytes();
4029 self.ensure_allocation_capacity(1, bytes)?;
4030 self.heap_bytes += bytes;
4031 let slot = self.heap.len() as u32 + 1;
4032 self.heap.push(entry);
4033 let id = SlotId::from_parts(RUNTIME_HEAP_SEGMENT, slot)
4034 .expect("runtime segment and one-based slot are nonzero");
4035 Ok(Value::heap_ref(id))
4036 }
4037
4038 fn ensure_allocation_capacity(
4039 &self,
4040 additional_slots: usize,
4041 additional_bytes: usize,
4042 ) -> Result<(), RuntimeErrorKind> {
4043 let used_slots = self.heap.len().saturating_sub(self.intrinsic_slots);
4044 let slots_fit_limit = used_slots
4045 .checked_add(additional_slots)
4046 .is_some_and(|total| total <= self.limits.max_heap_slots);
4047 let slots_fit_value = self
4048 .heap
4049 .len()
4050 .checked_add(additional_slots)
4051 .is_some_and(|total| total <= u32::MAX as usize);
4052 if !slots_fit_limit || !slots_fit_value {
4053 return Err(RuntimeErrorKind::HeapSlotLimitExceeded {
4054 limit: self.limits.max_heap_slots,
4055 });
4056 }
4057 let bytes_fit = self
4058 .heap_bytes
4059 .checked_add(additional_bytes)
4060 .is_some_and(|total| total <= self.limits.max_heap_bytes);
4061 if !bytes_fit {
4062 return Err(RuntimeErrorKind::HeapByteLimitExceeded {
4063 limit: self.limits.max_heap_bytes,
4064 });
4065 }
4066 Ok(())
4067 }
4068
4069 fn ensure_object_property_capacity(
4070 &self,
4071 property_bytes: usize,
4072 ) -> Result<(), RuntimeErrorKind> {
4073 let bytes =
4074 property_bytes
4075 .checked_add(1)
4076 .ok_or(RuntimeErrorKind::HeapByteLimitExceeded {
4077 limit: self.limits.max_heap_bytes,
4078 })?;
4079 self.ensure_allocation_capacity(1, bytes)
4080 }
4081 fn charge_heap(&mut self, bytes: usize) -> Result<(), RuntimeErrorKind> {
4082 self.ensure_allocation_capacity(0, bytes)?;
4083 self.heap_bytes += bytes;
4084 Ok(())
4085 }
4086
4087 fn runtime_slot(&self, value: Value) -> Result<Option<usize>, RuntimeErrorKind> {
4088 let Some(decoded) = value.decode() else {
4089 return Err(RuntimeErrorKind::InvalidValue { value });
4090 };
4091 let Decoded::HeapRef(id) = decoded else {
4092 return Ok(None);
4093 };
4094 if id.segment() != RUNTIME_HEAP_SEGMENT {
4095 return Err(RuntimeErrorKind::InvalidValue { value });
4096 }
4097 let index = id.slot() as usize - 1;
4098 if index >= self.heap.len() {
4099 return Err(RuntimeErrorKind::InvalidRuntimeHeapReference { slot: id.slot() });
4100 }
4101 Ok(Some(index))
4102 }
4103
4104 fn active_module_id(&self) -> ModuleId {
4105 self.frames
4106 .last()
4107 .map_or(ModuleId::new(0), |frame| frame.module)
4108 }
4109
4110 pub(crate) fn load_global(
4111 &self,
4112 module: ModuleId,
4113 name: ConstantId,
4114 ) -> Result<Option<Value>, RuntimeErrorKind> {
4115 if let Some(cell) = self
4116 .registry
4117 .modules
4118 .get(module.get() as usize)
4119 .and_then(|instance| instance.constant_cells.get(name.get() as usize))
4120 .copied()
4121 .flatten()
4122 {
4123 let value = self.registry.cells[cell.0].value;
4124 if value.is_uninitialized() {
4125 let binding = self.registry.modules[module.get() as usize]
4126 .binding_cells
4127 .iter()
4128 .position(|candidate| *candidate == Some(cell))
4129 .map(|index| BindingId::new(index as u32))
4130 .expect("linked cell belongs to a binding");
4131 return Err(RuntimeErrorKind::TemporalDeadZone { module, binding });
4132 }
4133 return Ok(Some(value));
4134 }
4135 Ok(self.resolve_global_binding(self.constant_text(module, name)))
4136 }
4137
4138 pub(crate) fn store_global(
4139 &mut self,
4140 module: ModuleId,
4141 name: ConstantId,
4142 value: Value,
4143 ) -> Result<(), EvalFailure> {
4144 let cell = self
4145 .registry
4146 .modules
4147 .get(module.get() as usize)
4148 .and_then(|instance| instance.constant_cells.get(name.get() as usize))
4149 .copied()
4150 .flatten();
4151 if let Some(cell) = cell {
4152 let binding = self.registry.modules[module.get() as usize]
4153 .binding_cells
4154 .iter()
4155 .position(|candidate| *candidate == Some(cell))
4156 .expect("mapped module cell belongs to a binding");
4157 if matches!(
4158 self.program_module(module).bindings[binding].kind,
4159 BindingKind::Imported { .. } | BindingKind::Namespace { .. }
4160 ) {
4161 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4162 operation: "assign to immutable module binding",
4163 }));
4164 }
4165 self.registry.cells[cell.0].value = value;
4166 } else {
4167 let name = self.constant_text(module, name).to_owned();
4168 if let Some(global_this) = self.intrinsics.global("globalThis") {
4169 let key = PropertyKey::Named(name.clone());
4170 if matches!(
4171 self.own_descriptor(global_this, &key)?,
4172 Some(
4173 Property::Data {
4174 writable: false,
4175 ..
4176 } | Property::Accessor { setter: None, .. }
4177 )
4178 ) {
4179 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4180 operation: "assign to non-writable global property",
4181 }));
4182 }
4183 }
4184 self.globals.insert(name, value);
4185 }
4186 Ok(())
4187 }
4188
4189 fn resolve_global_binding(&self, name: &EcmaString) -> Option<Value> {
4191 self.globals.get(name).copied().or_else(|| {
4192 self.intrinsics
4193 .globals
4194 .iter()
4195 .find_map(|(candidate, value)| (candidate == name).then_some(*value))
4196 })
4197 }
4198
4199 fn callee_kind(&self, callee: Value) -> Result<CalleeKind, RuntimeErrorKind> {
4201 match self.runtime_slot(callee)? {
4202 Some(index) => match &self.heap[index] {
4203 HeapEntry::Function {
4204 module,
4205 function,
4206 captures,
4207 ..
4208 } => Ok(CalleeKind::Runtime {
4209 target: RuntimeFunction {
4210 module: *module,
4211 function: *function,
4212 },
4213 captures: captures.clone(),
4214 }),
4215 HeapEntry::NativeFunction { callable, .. } => match callable {
4216 NativeCallable::Builtin(id) => Ok(CalleeKind::Builtin { id: *id }),
4217 NativeCallable::Bound(_) => Ok(CalleeKind::Bound),
4218 },
4219 _ => Ok(CalleeKind::NotCallable),
4220 },
4221 None => Ok(CalleeKind::NotCallable),
4222 }
4223 }
4224
4225 pub(crate) fn flatten_bound(
4226 &self,
4227 callee: Value,
4228 this_value: Value,
4229 arguments: &[Value],
4230 ) -> Result<BoundCall, RuntimeErrorKind> {
4231 let mut target = callee;
4232 let mut receiver = this_value;
4233 let mut segments = Vec::new();
4234 let mut total = arguments.len();
4235 while let Some(index) = self.runtime_slot(target)? {
4236 let HeapEntry::NativeFunction {
4237 callable: NativeCallable::Bound(bound),
4238 ..
4239 } = &self.heap[index]
4240 else {
4241 break;
4242 };
4243 total = total.checked_add(bound.arguments.len()).ok_or(
4244 RuntimeErrorKind::ArgumentLimitExceeded {
4245 limit: self.limits.max_argument_count,
4246 requested: u32::MAX,
4247 },
4248 )?;
4249 if total > self.limits.max_argument_count as usize {
4250 return Err(RuntimeErrorKind::ArgumentLimitExceeded {
4251 limit: self.limits.max_argument_count,
4252 requested: u32::try_from(total).unwrap_or(u32::MAX),
4253 });
4254 }
4255 segments.push(bound.arguments.as_slice());
4256 receiver = bound.this_value;
4257 target = bound.target;
4258 }
4259 let mut flattened = Vec::with_capacity(total);
4260 for segment in segments.iter().rev() {
4261 flattened.extend_from_slice(segment);
4262 }
4263 flattened.extend_from_slice(arguments);
4264 Ok(BoundCall {
4265 target,
4266 this_value: receiver,
4267 arguments: flattened,
4268 })
4269 }
4270
4271 fn bound_target(&self, mut value: Value) -> Result<Value, RuntimeErrorKind> {
4272 loop {
4273 let Some(index) = self.runtime_slot(value)? else {
4274 return Ok(value);
4275 };
4276 let HeapEntry::NativeFunction {
4277 callable: NativeCallable::Bound(bound),
4278 ..
4279 } = &self.heap[index]
4280 else {
4281 return Ok(value);
4282 };
4283 value = bound.target;
4284 }
4285 }
4286
4287 pub(crate) fn load_constant_value(
4290 &mut self,
4291 module: ModuleId,
4292 id: ConstantId,
4293 ) -> Result<Value, RuntimeErrorKind> {
4294 match &self.module_code(module).constants()[id.get() as usize] {
4295 Constant::String(text) => self.allocate(HeapEntry::String(text.clone())),
4296 Constant::BigInt(value) => self.allocate(HeapEntry::BigInt(value.as_str().to_owned())),
4297 constant => Ok(constant_value(constant).expect("non-heap constant")),
4298 }
4299 }
4300
4301 fn read_arguments(&self, frame: usize, register: u32) -> Result<Vec<Value>, EvalFailure> {
4304 let value = self.read_register(frame, register);
4305 self.arguments_from_array(value)
4306 }
4307
4308 fn arguments_from_array(&self, arguments: Value) -> Result<Vec<Value>, EvalFailure> {
4312 match self.runtime_slot(arguments).map_err(EvalFailure::Runtime)? {
4313 Some(index) => match &self.heap[index] {
4314 HeapEntry::Array { elements, .. } => {
4315 if elements.len() as u64 > u64::from(self.limits.max_argument_count) {
4316 return Err(EvalFailure::Runtime(
4317 RuntimeErrorKind::ArgumentLimitExceeded {
4318 limit: self.limits.max_argument_count,
4319 requested: u32::try_from(elements.len()).unwrap_or(u32::MAX),
4320 },
4321 ));
4322 }
4323 Ok(elements
4324 .iter()
4325 .map(|value| {
4326 if *value == Value::HOLE {
4327 Value::UNDEFINED
4328 } else {
4329 *value
4330 }
4331 })
4332 .collect())
4333 }
4334 _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4335 operation: "call arguments are not an array",
4336 })),
4337 },
4338 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4339 operation: "call arguments are not an array",
4340 })),
4341 }
4342 }
4343
4344 fn read_captures(
4347 &self,
4348 frame: usize,
4349 register: u32,
4350 function: FunctionId,
4351 ) -> Result<Vec<Value>, EvalFailure> {
4352 let value = self.read_register(frame, register);
4353 self.captures_from_array(self.active_module_id(), value, function)
4354 }
4355
4356 pub(crate) fn captures_from_array(
4360 &self,
4361 module: ModuleId,
4362 captures: Value,
4363 function: FunctionId,
4364 ) -> Result<Vec<Value>, EvalFailure> {
4365 let expected =
4366 self.module_code(module).functions()[function.get() as usize].capture_count() as usize;
4367 match self.runtime_slot(captures).map_err(EvalFailure::Runtime)? {
4368 Some(index) => match &self.heap[index] {
4369 HeapEntry::Array { elements, .. } => {
4370 if elements.len() != expected {
4371 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4372 operation: "closure capture array arity",
4373 }));
4374 }
4375 Ok(elements
4376 .iter()
4377 .map(|value| {
4378 if *value == Value::HOLE {
4379 Value::UNDEFINED
4380 } else {
4381 *value
4382 }
4383 })
4384 .collect())
4385 }
4386 _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4387 operation: "closure captures are not an array",
4388 })),
4389 },
4390 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4391 operation: "closure captures are not an array",
4392 })),
4393 }
4394 }
4395
4396 pub(crate) fn materialize_arguments(
4397 &mut self,
4398 frame: usize,
4399 function: usize,
4400 pc: usize,
4401 ) -> Result<Value, RuntimeError> {
4402 if let Some(existing) = self.frames[frame].arguments_object {
4403 return Ok(existing);
4404 }
4405 let args = self.frames[frame].args.clone();
4406 let value = self
4407 .allocate(HeapEntry::Array {
4408 elements: args,
4409 properties: PropertyMap::default(),
4410 prototype: Some(self.intrinsics.array_prototype),
4411 extensible: true,
4412 length_writable: true,
4413 })
4414 .map_err(|kind| self.error_at(kind, function, pc))?;
4415 self.frames[frame].arguments_object = Some(value);
4416 Ok(value)
4417 }
4418
4419 fn push_frame(
4420 &mut self,
4421 target: RuntimeFunction,
4422 captures: &[Value],
4423 this_value: Value,
4424 new_target: Value,
4425 arguments: &[Value],
4426 return_to: Option<ReturnTo>,
4427 ) -> Result<(), RuntimeError> {
4428 let function_index = target.function.get() as usize;
4429 let metadata = &self.module_code(target.module).functions()[function_index];
4430 let limit_error = |kind| match (self.frames.last(), return_to) {
4431 (Some(caller), Some(return_to)) => {
4432 self.error_at_in_module(kind, caller.module, caller.function, return_to.call_pc)
4433 }
4434 (_, None) => self.error_at_in_module(kind, target.module, function_index, 0),
4435 (None, Some(_)) => unreachable!("a returning frame has a caller"),
4436 };
4437 if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4438 return Err(limit_error(RuntimeErrorKind::CallDepthExceeded {
4439 limit: self.limits.max_call_depth,
4440 }));
4441 }
4442 let next_registers = metadata.register_count() as usize;
4443 if self.live_registers.saturating_add(next_registers) > self.limits.max_total_registers {
4444 return Err(limit_error(RuntimeErrorKind::RegisterLimitExceeded {
4445 limit: self.limits.max_total_registers,
4446 }));
4447 }
4448 let frame = Frame::new(
4449 target, metadata, captures, this_value, new_target, arguments, return_to,
4450 );
4451 self.live_registers += next_registers;
4452 self.frames.push(frame);
4453 Ok(())
4454 }
4455
4456 pub(crate) fn consume_fuel(&mut self, amount: u64) -> Result<(), RuntimeErrorKind> {
4457 if self.fuel < amount {
4458 self.fuel = 0;
4459 return Err(RuntimeErrorKind::FuelExhausted {
4460 limit: self.limits.fuel,
4461 });
4462 }
4463 self.fuel -= amount;
4464 Ok(())
4465 }
4466
4467 pub(crate) fn reserve_native_activation(
4468 &mut self,
4469 register_count: usize,
4470 ) -> Result<(), RuntimeErrorKind> {
4471 if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4472 return Err(RuntimeErrorKind::CallDepthExceeded {
4473 limit: self.limits.max_call_depth,
4474 });
4475 }
4476 if self.live_registers.saturating_add(register_count) > self.limits.max_total_registers {
4477 return Err(RuntimeErrorKind::RegisterLimitExceeded {
4478 limit: self.limits.max_total_registers,
4479 });
4480 }
4481 self.native_depth += 1;
4482 self.live_registers += register_count;
4483 Ok(())
4484 }
4485
4486 pub(crate) fn release_native_activation(&mut self, register_count: usize) {
4487 self.native_depth -= 1;
4488 self.live_registers -= register_count;
4489 }
4490
4491 pub(crate) fn reserve_suspended_activation_registers(
4492 &mut self,
4493 register_count: usize,
4494 ) -> Result<(), RuntimeErrorKind> {
4495 if self.live_registers.saturating_add(register_count) > self.limits.max_total_registers {
4496 return Err(RuntimeErrorKind::RegisterLimitExceeded {
4497 limit: self.limits.max_total_registers,
4498 });
4499 }
4500 self.live_registers += register_count;
4501 Ok(())
4502 }
4503
4504 pub(crate) fn release_suspended_activation_registers(&mut self, register_count: usize) {
4505 self.live_registers -= register_count;
4506 }
4507
4508 pub(crate) fn enter_native_generator(&mut self) -> Result<(), RuntimeErrorKind> {
4509 if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4510 return Err(RuntimeErrorKind::CallDepthExceeded {
4511 limit: self.limits.max_call_depth,
4512 });
4513 }
4514 self.native_depth += 1;
4515 Ok(())
4516 }
4517
4518 pub(crate) fn leave_native_generator(&mut self) {
4519 self.native_depth -= 1;
4520 }
4521
4522 fn execute_call(&mut self, request: CallRequest<'_>) -> Result<(), RuntimeError> {
4523 let CallRequest {
4524 callee,
4525 this_value,
4526 arguments,
4527 destination,
4528 call_pc,
4529 constructed,
4530 new_target,
4531 } = request;
4532 let mut callee = callee;
4533 let mut this_value = this_value;
4534 let mut arguments = Cow::Borrowed(arguments);
4535 loop {
4536 match self.callee_kind(callee) {
4537 Ok(CalleeKind::Runtime { target, captures }) => {
4538 let flags = self.module_code(target.module).functions()
4539 [target.function.get() as usize]
4540 .flags();
4541 if flags.is_generator && !flags.is_async {
4542 let generator = self
4543 .create_generator(GeneratorStart {
4544 target,
4545 captures,
4546 this_value,
4547 new_target,
4548 args: arguments.as_ref().to_vec(),
4549 })
4550 .map_err(|kind| self.error_here_at(kind, call_pc))?;
4551 if let Some(register) = destination {
4552 self.write_register(self.frames.len() - 1, register, generator);
4553 }
4554 return Ok(());
4555 }
4556 if flags.is_async && !flags.is_generator {
4557 return match self.start_async_call(
4558 target,
4559 &captures,
4560 this_value,
4561 new_target,
4562 arguments.as_ref(),
4563 ) {
4564 Ok(promise) => {
4565 if let Some(register) = destination {
4566 self.write_register(self.frames.len() - 1, register, promise);
4567 }
4568 Ok(())
4569 }
4570 Err(failure) => self.resolve_failure(failure, call_pc),
4571 };
4572 }
4573 return self.push_frame(
4574 target,
4575 &captures,
4576 this_value,
4577 new_target,
4578 arguments.as_ref(),
4579 Some(ReturnTo {
4580 destination: destination.map(|register| register as usize),
4581 call_pc,
4582 constructed,
4583 }),
4584 );
4585 }
4586 Ok(CalleeKind::Builtin { id }) => {
4587 match self.call_builtin(id, this_value, arguments.as_ref(), false) {
4588 Ok(intrinsics::BuiltinOutcome::Value(value)) => {
4589 if let Some(register) = destination {
4590 self.write_register(self.frames.len() - 1, register, value);
4591 }
4592 return Ok(());
4593 }
4594 Ok(intrinsics::BuiltinOutcome::Call {
4595 callee: next,
4596 this_value: next_this,
4597 arguments: next_arguments,
4598 }) => {
4599 callee = next;
4600 this_value = next_this;
4601 arguments = Cow::Owned(next_arguments);
4602 }
4603 Ok(intrinsics::BuiltinOutcome::GeneratorNext {
4604 generator,
4605 resume_value,
4606 }) => match self.resume_generator(generator, resume_value) {
4607 Ok(value) => {
4608 if let Some(register) = destination {
4609 self.write_register(self.frames.len() - 1, register, value);
4610 }
4611 return Ok(());
4612 }
4613 Err(failure) => return self.resolve_failure(failure, call_pc),
4614 },
4615 Ok(intrinsics::BuiltinOutcome::ConstructCall { .. }) => {
4616 return self.throw_type("call", call_pc);
4617 }
4618 Err(failure) => return self.resolve_failure(failure, call_pc),
4619 }
4620 }
4621 Ok(CalleeKind::Bound) => {
4622 let bound = self
4623 .flatten_bound(callee, this_value, arguments.as_ref())
4624 .map_err(|kind| self.error_here_at(kind, call_pc))?;
4625 callee = bound.target;
4626 if constructed.is_none() {
4627 this_value = bound.this_value;
4628 }
4629 arguments = Cow::Owned(bound.arguments);
4630 }
4631 Ok(CalleeKind::NotCallable) => return self.throw_type("call", call_pc),
4632 Err(kind) => return Err(self.error_here_at(kind, call_pc)),
4633 }
4634 }
4635 }
4636
4637 fn execute_construct(
4638 &mut self,
4639 callee: Value,
4640 arguments: &[Value],
4641 destination: u32,
4642 call_pc: usize,
4643 ) -> Result<(), RuntimeError> {
4644 let mut callee = callee;
4645 let mut arguments = Cow::Borrowed(arguments);
4646 if matches!(self.callee_kind(callee), Ok(CalleeKind::Bound)) {
4647 let bound = self
4648 .flatten_bound(callee, Value::UNDEFINED, arguments.as_ref())
4649 .map_err(|kind| self.error_here_at(kind, call_pc))?;
4650 callee = bound.target;
4651 arguments = Cow::Owned(bound.arguments);
4652 }
4653 let index = match self.runtime_slot(callee) {
4654 Ok(Some(index)) => index,
4655 Ok(None) => return self.throw_type("construct", call_pc),
4656 Err(kind) => return Err(self.error_here_at(kind, call_pc)),
4657 };
4658 let builtin = match &self.heap[index] {
4659 HeapEntry::NativeFunction {
4660 callable: NativeCallable::Builtin(id),
4661 ..
4662 } => Some(*id),
4663 _ => None,
4664 };
4665 if let Some(id) = builtin {
4666 return match self.call_builtin(id, Value::UNDEFINED, arguments.as_ref(), true) {
4667 Ok(intrinsics::BuiltinOutcome::Value(value)) => {
4668 self.write_register(self.frames.len() - 1, destination, value);
4669 Ok(())
4670 }
4671 Ok(
4672 intrinsics::BuiltinOutcome::Call { .. }
4673 | intrinsics::BuiltinOutcome::GeneratorNext { .. },
4674 ) => self.throw_type("construct", call_pc),
4675 Ok(intrinsics::BuiltinOutcome::ConstructCall {
4676 callee: continuation,
4677 this_value,
4678 arguments: continuation_arguments,
4679 prototype,
4680 }) => {
4681 let object = self
4682 .allocate_constructed_receiver_with(prototype)
4683 .map_err(|kind| self.error_here_at(kind, call_pc))?;
4684 self.execute_call(CallRequest {
4685 callee: continuation,
4686 this_value,
4687 arguments: &continuation_arguments,
4688 destination: Some(destination),
4689 call_pc,
4690 constructed: Some(object),
4691 new_target: callee,
4692 })
4693 }
4694 Err(failure) => self.resolve_failure(failure, call_pc),
4695 };
4696 }
4697 if !matches!(
4698 self.heap[index],
4699 HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. }
4700 ) {
4701 return self.throw_type("construct", call_pc);
4702 }
4703 if let HeapEntry::Function {
4704 module, function, ..
4705 } = self.heap[index]
4706 {
4707 if self.module_code(module).functions()[function.get() as usize]
4708 .flags()
4709 .is_async
4710 {
4711 return self.throw_type("construct", call_pc);
4712 }
4713 }
4714 let object = self
4715 .allocate_constructed_receiver(callee)
4716 .map_err(|kind| self.error_here_at(kind, call_pc))?;
4717 self.execute_call(CallRequest {
4718 callee,
4719 this_value: object,
4720 arguments: arguments.as_ref(),
4721 destination: Some(destination),
4722 call_pc,
4723 constructed: Some(object),
4724 new_target: callee,
4725 })
4726 }
4727
4728 fn constructed_prototype(&self, callee: Value) -> Result<Value, RuntimeErrorKind> {
4729 let index = self
4730 .runtime_slot(callee)?
4731 .ok_or(RuntimeErrorKind::InvalidValue { value: callee })?;
4732 Ok(match self.own_data_property(index, "prototype") {
4733 Some(value) if self.is_object(value) => value,
4734 _ => self.intrinsics.object_prototype,
4735 })
4736 }
4737
4738 fn allocate_constructed_receiver(&mut self, callee: Value) -> Result<Value, RuntimeErrorKind> {
4739 let prototype = self.constructed_prototype(callee)?;
4740 self.allocate_constructed_receiver_with(prototype)
4741 }
4742
4743 fn allocate_constructed_receiver_with(
4744 &mut self,
4745 prototype: Value,
4746 ) -> Result<Value, RuntimeErrorKind> {
4747 self.allocate(HeapEntry::Object {
4748 properties: PropertyMap::default(),
4749 prototype: Some(prototype),
4750 boxed_primitive: None,
4751 extensible: true,
4752 })
4753 }
4754
4755 pub(crate) fn array_elements(&self, value: Value) -> Result<Option<Vec<Value>>, EvalFailure> {
4756 let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4757 return Ok(None);
4758 };
4759 match &self.heap[index] {
4760 HeapEntry::Array { elements, .. } => Ok(Some(elements.clone())),
4761 _ => Ok(None),
4762 }
4763 }
4764
4765 pub(crate) fn array_length(&self, value: Value) -> Result<usize, EvalFailure> {
4766 self.array_elements(value)?
4767 .map(|elements| elements.len())
4768 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
4769 operation: "array method called on incompatible receiver",
4770 }))
4771 }
4772
4773 pub(crate) fn replace_array_elements(
4774 &mut self,
4775 value: Value,
4776 elements: Vec<Value>,
4777 ) -> Result<(), EvalFailure> {
4778 let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4779 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4780 operation: "array method called on incompatible receiver",
4781 }));
4782 };
4783 let HeapEntry::Array {
4784 elements: current, ..
4785 } = &mut self.heap[index]
4786 else {
4787 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4788 operation: "array method called on incompatible receiver",
4789 }));
4790 };
4791 *current = elements;
4792 Ok(())
4793 }
4794
4795 pub(crate) fn string_value(&self, value: Value) -> Option<EcmaString> {
4796 let index = self.runtime_slot(value).ok().flatten()?;
4797 match &self.heap[index] {
4798 HeapEntry::String(text) => Some(text.clone()),
4799 _ => None,
4800 }
4801 }
4802
4803 pub(crate) fn get_named_property(
4804 &mut self,
4805 object: Value,
4806 name: &str,
4807 ) -> Result<Value, EvalFailure> {
4808 self.get_property_ascii(object, name)
4809 }
4810
4811 fn get_property_ascii(&mut self, object: Value, name: &str) -> Result<Value, EvalFailure> {
4812 debug_assert!(name.is_ascii());
4813 match self.resolve_get_ascii(object, name)? {
4814 GetOutcome::Value(value) => Ok(value),
4815 GetOutcome::Text(text) => self
4816 .allocate(HeapEntry::String(text))
4817 .map_err(EvalFailure::Runtime),
4818 GetOutcome::Getter(getter) => self.call_value(getter, object, &[]),
4819 }
4820 }
4821
4822 pub(crate) fn get_property_key(
4823 &mut self,
4824 object: Value,
4825 key: &PropertyKey,
4826 ) -> Result<Value, EvalFailure> {
4827 match self.resolve_get(object, key)? {
4828 GetOutcome::Value(value) => Ok(value),
4829 GetOutcome::Text(text) => self
4830 .allocate(HeapEntry::String(text))
4831 .map_err(EvalFailure::Runtime),
4832 GetOutcome::Getter(getter) => self.call_value(getter, object, &[]),
4833 }
4834 }
4835
4836 pub(crate) fn set_data_property(
4837 &mut self,
4838 object: Value,
4839 name: &str,
4840 value: Value,
4841 ) -> Result<(), EvalFailure> {
4842 self.set_data_property_key(
4843 object,
4844 PropertyKey::Named(EcmaString::from_utf8(name)),
4845 value,
4846 )
4847 }
4848
4849 pub(crate) fn set_data_property_key(
4850 &mut self,
4851 object: Value,
4852 key: PropertyKey,
4853 value: Value,
4854 ) -> Result<(), EvalFailure> {
4855 match self.resolve_set(object, key, value)? {
4856 SetOutcome::Done => Ok(()),
4857 SetOutcome::Setter(setter) => {
4858 self.call_value(setter, object, &[value])?;
4859 Ok(())
4860 }
4861 }
4862 }
4863
4864 pub(crate) fn is_callable(&self, value: Value) -> Result<bool, EvalFailure> {
4865 Ok(!matches!(
4866 self.callee_kind(value).map_err(EvalFailure::Runtime)?,
4867 CalleeKind::NotCallable
4868 ))
4869 }
4870
4871 pub(crate) fn box_primitive(&mut self, value: Value) -> Result<Value, EvalFailure> {
4872 let prototype = match value.decode() {
4873 Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
4874 Some(Decoded::Number(_) | Decoded::Int32(_)) => self.intrinsics.number_prototype,
4875 Some(Decoded::HeapRef(_)) if self.string_value(value).is_some() => {
4876 self.intrinsics.string_prototype
4877 }
4878 _ => self.intrinsics.object_prototype,
4879 };
4880 self.allocate(HeapEntry::Object {
4881 properties: PropertyMap::default(),
4882 prototype: Some(prototype),
4883 boxed_primitive: Some(value),
4884 extensible: true,
4885 })
4886 .map_err(EvalFailure::Runtime)
4887 }
4888
4889 pub(crate) fn unbox_primitive_or_self(&self, value: Value) -> Result<Value, EvalFailure> {
4890 let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4891 return Ok(value);
4892 };
4893 match self.heap[index] {
4894 HeapEntry::Object {
4895 boxed_primitive: Some(primitive),
4896 ..
4897 } => Ok(primitive),
4898 _ => Ok(value),
4899 }
4900 }
4901
4902 pub(crate) fn unbox_primitive(
4903 &self,
4904 value: Value,
4905 operation: &'static str,
4906 ) -> Result<Value, EvalFailure> {
4907 let unboxed = self.unbox_primitive_or_self(value)?;
4908 if unboxed == value && self.is_object(value) {
4909 Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }))
4910 } else {
4911 Ok(unboxed)
4912 }
4913 }
4914
4915 pub(crate) fn current_builtin_id(&self) -> Option<intrinsics::BuiltinId> {
4916 self.current_builtin_id
4917 }
4918
4919 pub(crate) fn throw_error(
4920 &mut self,
4921 id: intrinsics::BuiltinId,
4922 message: String,
4923 ) -> EvalFailure {
4924 let message = match self.allocate(HeapEntry::String(EcmaString::from_utf8(&message))) {
4925 Ok(value) => value,
4926 Err(kind) => return EvalFailure::Runtime(kind),
4927 };
4928 let mut properties = PropertyMap::default();
4929 properties.insert(
4930 PropertyKey::Named(EcmaString::from_utf8("message")),
4931 Property::Data {
4932 value: message,
4933 writable: true,
4934 enumerable: true,
4935 configurable: true,
4936 },
4937 );
4938 match self.allocate(HeapEntry::Object {
4939 properties,
4940 prototype: Some(self.intrinsics.error_prototype(id)),
4941 boxed_primitive: None,
4942 extensible: true,
4943 }) {
4944 Ok(value) => EvalFailure::ThrowValue(value),
4945 Err(kind) => EvalFailure::Runtime(kind),
4946 }
4947 }
4948
4949 pub(crate) fn has_own_property_key(
4950 &self,
4951 object: Value,
4952 key: &PropertyKey,
4953 ) -> Result<bool, EvalFailure> {
4954 let Some(index) = self.runtime_slot(object).map_err(EvalFailure::Runtime)? else {
4955 return Ok(false);
4956 };
4957 Ok(self.own_get(index, key).is_some())
4958 }
4959
4960 pub(crate) fn call_value(
4961 &mut self,
4962 callee: Value,
4963 this_value: Value,
4964 arguments: &[Value],
4965 ) -> Result<Value, EvalFailure> {
4966 let mut callee = callee;
4967 let mut this_value = this_value;
4968 let mut arguments = Cow::Borrowed(arguments);
4969 loop {
4970 match self.callee_kind(callee).map_err(EvalFailure::Runtime)? {
4971 CalleeKind::Builtin { id } => {
4972 match self.call_builtin(id, this_value, arguments.as_ref(), false)? {
4973 intrinsics::BuiltinOutcome::Value(value) => return Ok(value),
4974 intrinsics::BuiltinOutcome::Call {
4975 callee: next,
4976 this_value: next_this,
4977 arguments: next_arguments,
4978 } => {
4979 callee = next;
4980 this_value = next_this;
4981 arguments = Cow::Owned(next_arguments);
4982 }
4983 intrinsics::BuiltinOutcome::GeneratorNext {
4984 generator,
4985 resume_value,
4986 } => return self.resume_generator(generator, resume_value),
4987 intrinsics::BuiltinOutcome::ConstructCall { .. } => {
4988 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4989 operation: "call",
4990 }));
4991 }
4992 }
4993 }
4994 CalleeKind::Runtime { target, captures } => {
4995 let flags = self.module_code(target.module).functions()
4996 [target.function.get() as usize]
4997 .flags();
4998 if flags.is_generator && !flags.is_async {
4999 return self
5000 .create_generator(GeneratorStart {
5001 target,
5002 captures,
5003 this_value,
5004 new_target: Value::UNDEFINED,
5005 args: arguments.as_ref().to_vec(),
5006 })
5007 .map_err(EvalFailure::Runtime);
5008 }
5009 if flags.is_async && !flags.is_generator {
5010 return self.start_async_call(
5011 target,
5012 &captures,
5013 this_value,
5014 Value::UNDEFINED,
5015 arguments.as_ref(),
5016 );
5017 }
5018 let stop_depth = self.frames.len();
5019 let return_to = self.frames.last().map(|frame| ReturnTo {
5020 destination: None,
5021 call_pc: frame.pc,
5022 constructed: None,
5023 });
5024 self.push_frame(
5025 target,
5026 &captures,
5027 this_value,
5028 Value::UNDEFINED,
5029 arguments.as_ref(),
5030 return_to,
5031 )
5032 .map_err(|error| EvalFailure::Runtime(error.kind))?;
5033 self.callback_boundaries.push(stop_depth);
5034 let result = self.run_loop(stop_depth);
5035 self.callback_boundaries
5036 .pop()
5037 .expect("nested runtime callback owns its unwind boundary");
5038 return match result {
5039 Ok(None) => self.last_completion.take().ok_or(EvalFailure::Runtime(
5040 RuntimeErrorKind::InvalidValue {
5041 value: Value::UNDEFINED,
5042 },
5043 )),
5044 Ok(Some(execution)) => Ok(execution.value),
5045 Err(error) => {
5046 self.unwind_frames_to(stop_depth);
5047 match error.kind {
5048 RuntimeErrorKind::UncaughtThrow { value, .. } => {
5049 Err(EvalFailure::ThrowValue(value))
5050 }
5051 kind => Err(EvalFailure::Runtime(kind)),
5052 }
5053 }
5054 };
5055 }
5056 CalleeKind::Bound => {
5057 let bound = self
5058 .flatten_bound(callee, this_value, arguments.as_ref())
5059 .map_err(EvalFailure::Runtime)?;
5060 callee = bound.target;
5061 this_value = bound.this_value;
5062 arguments = Cow::Owned(bound.arguments);
5063 }
5064 CalleeKind::NotCallable => {
5065 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5066 operation: "call",
5067 }));
5068 }
5069 }
5070 }
5071 }
5072
5073 fn unwind_frames_to(&mut self, depth: usize) {
5074 while self.frames.len() > depth {
5075 let frame = self.frames.pop().expect("frame depth was checked");
5076 self.live_registers -= frame.registers.len();
5077 }
5078 }
5079
5080 fn complete_frame(&mut self, returned: Value) -> Option<Execution> {
5081 let frame = self.frames.pop().expect("an activation is executing");
5082 self.live_registers -= frame.registers.len();
5083 match frame.return_to {
5084 None => {
5085 let outcome = ExecutionOutcome {
5086 stdout: Vec::new(),
5087 exit_code: 0,
5088 };
5089 Some(Execution {
5090 outcome,
5091 value: returned,
5092 link: returned,
5093 entry_registers: frame.registers,
5094 })
5095 }
5096 Some(return_to) => {
5097 let value = match return_to.constructed {
5098 Some(object) if !self.is_object(returned) => object,
5099 _ => returned,
5100 };
5101 if let Some(destination) = return_to.destination {
5102 self.frames.last_mut().expect("callee has caller").registers[destination] =
5103 value;
5104 } else {
5105 self.last_completion = Some(value);
5106 }
5107 None
5108 }
5109 }
5110 }
5111
5112 fn resolve_failure(&mut self, failure: EvalFailure, pc: usize) -> Result<(), RuntimeError> {
5113 match failure {
5114 EvalFailure::Throw(origin) => self.throw(Value::UNDEFINED, origin, pc),
5115 EvalFailure::ThrowValue(value) => self.throw(value, ThrowOrigin::Bytecode, pc),
5116 EvalFailure::ThrowValueOrigin { value, origin } => self.throw(value, origin, pc),
5117 EvalFailure::Runtime(kind) => Err(self.error_here_at(kind, pc)),
5118 }
5119 }
5120
5121 fn throw_type(&mut self, operation: &'static str, pc: usize) -> Result<(), RuntimeError> {
5122 self.throw(Value::UNDEFINED, ThrowOrigin::TypeError { operation }, pc)
5123 }
5124
5125 fn throw(
5126 &mut self,
5127 value: Value,
5128 origin: ThrowOrigin,
5129 faulting_pc: usize,
5130 ) -> Result<(), RuntimeError> {
5131 let site_module = self
5132 .frames
5133 .last()
5134 .expect("an activation is executing")
5135 .module;
5136 let site_function = self
5137 .frames
5138 .last()
5139 .expect("an activation is executing")
5140 .function;
5141 let mut search_pc = faulting_pc;
5142 loop {
5143 if self
5144 .callback_boundaries
5145 .last()
5146 .is_some_and(|boundary| self.frames.len() == *boundary)
5147 {
5148 return Err(self.error_at_in_module(
5149 RuntimeErrorKind::UncaughtThrow { value, origin },
5150 site_module,
5151 site_function,
5152 faulting_pc,
5153 ));
5154 }
5155 let frame_index = self.frames.len() - 1;
5156 let function_index = self.frames[frame_index].function;
5157 let module = self.frames[frame_index].module;
5158 let function = &self.module_code(module).functions()[function_index];
5159 if let Some(handler) = innermost_handler(function, search_pc) {
5160 let frame = &mut self.frames[frame_index];
5161 frame.registers[handler.catch_register.get() as usize] = value;
5162 frame.pc = handler.handler.get() as usize;
5163 return Ok(());
5164 }
5165 let frame = self.frames.pop().expect("throw walks live frames");
5166 self.live_registers -= frame.registers.len();
5167 match frame.return_to {
5168 Some(return_to) => search_pc = return_to.call_pc,
5169 None => {
5170 return Err(self.error_at_in_module(
5171 RuntimeErrorKind::UncaughtThrow { value, origin },
5172 site_module,
5173 site_function,
5174 faulting_pc,
5175 ));
5176 }
5177 }
5178 }
5179 }
5180
5181 fn error_here(&self, kind: RuntimeErrorKind) -> RuntimeError {
5182 let frame = self.frames.last().expect("an activation is executing");
5183 self.error_at(kind, frame.function, frame.pc)
5184 }
5185
5186 fn error_here_at(&self, kind: RuntimeErrorKind, pc: usize) -> RuntimeError {
5187 let function = self
5188 .frames
5189 .last()
5190 .expect("an activation is executing")
5191 .function;
5192 self.error_at(kind, function, pc)
5193 }
5194
5195 fn error_at(&self, kind: RuntimeErrorKind, function: usize, pc: usize) -> RuntimeError {
5196 self.error_at_in_module(kind, self.active_module_id(), function, pc)
5197 }
5198
5199 pub(crate) fn error_at_in_module(
5200 &self,
5201 kind: RuntimeErrorKind,
5202 module: ModuleId,
5203 function: usize,
5204 pc: usize,
5205 ) -> RuntimeError {
5206 let code = self.module_code(module);
5207 let metadata = &code.functions()[function];
5208 let function_name =
5209 metadata
5210 .name()
5211 .and_then(|id| match &code.constants()[id.get() as usize] {
5212 Constant::String(name) => Some(name.clone()),
5213 _ => None,
5214 });
5215 RuntimeError {
5216 kind,
5217 function: FunctionId::new(function as u32),
5218 pc: Pc::new(pc as u32),
5219 source: RuntimeSource {
5220 function_name,
5221 instruction: metadata.code()[pc],
5222 },
5223 }
5224 }
5225
5226 fn to_property_key(&self, value: Value) -> Result<PropertyKey, EvalFailure> {
5232 match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
5233 Some(index) => match &self.heap[index] {
5234 HeapEntry::String(text) => Ok(PropertyKey::Named(text.clone())),
5235 HeapEntry::Symbol { .. } => Ok(PropertyKey::Symbol(index as u32)),
5236 HeapEntry::PrivateName { .. } => Ok(PropertyKey::Private(index as u32)),
5237 _ => Ok(PropertyKey::Named(self.value_to_string(value, 0)?)),
5238 },
5239 None => Ok(PropertyKey::Named(self.value_to_string(value, 0)?)),
5240 }
5241 }
5242
5243 fn resolve_get(&mut self, object: Value, key: &PropertyKey) -> Result<GetOutcome, EvalFailure> {
5246 let slot = self.runtime_slot(object).map_err(EvalFailure::Runtime)?;
5247 let start = match slot {
5248 Some(index) => {
5249 if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5250 let PropertyKey::Named(name) = key else {
5251 return Ok(GetOutcome::Value(Value::UNDEFINED));
5252 };
5253 let text = name
5254 .to_utf8_strict()
5255 .ok()
5256 .and_then(|name| self.host.env(&name))
5257 .map(EcmaString::from_utf8);
5258 return match text {
5259 Some(text) => self
5260 .allocate(HeapEntry::String(text))
5261 .map(GetOutcome::Value)
5262 .map_err(EvalFailure::Runtime),
5263 None => Ok(GetOutcome::Value(Value::UNDEFINED)),
5264 };
5265 }
5266 if let Some(found) = self.primitive_get(index, key) {
5267 return self.found_outcome(found);
5268 }
5269 match self.heap[index] {
5270 HeapEntry::String(_) => self
5271 .runtime_slot(self.intrinsics.string_prototype)
5272 .map_err(EvalFailure::Runtime)?,
5273 HeapEntry::BigInt(_) | HeapEntry::PrivateName { .. } => self
5274 .runtime_slot(self.intrinsics.object_prototype)
5275 .map_err(EvalFailure::Runtime)?,
5276 HeapEntry::Symbol { .. } => self
5277 .runtime_slot(self.intrinsics.builtins.symbol_prototype())
5278 .map_err(EvalFailure::Runtime)?,
5279 _ => Some(index),
5280 }
5281 }
5282 None => {
5283 let prototype = match object.decode() {
5284 Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
5285 Some(Decoded::Number(_) | Decoded::Int32(_)) => {
5286 self.intrinsics.number_prototype
5287 }
5288 _ => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5289 };
5290 self.runtime_slot(prototype).map_err(EvalFailure::Runtime)?
5291 }
5292 };
5293 let Some(mut node) = start else {
5294 return Ok(GetOutcome::Value(Value::UNDEFINED));
5295 };
5296 for _ in 0..=self.heap.len() {
5297 if let Some(found) = self.own_get(node, key) {
5298 return self.found_outcome(found);
5299 }
5300 match self.prototype_index(node)? {
5301 Some(next) => node = next,
5302 None => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5303 }
5304 }
5305 Ok(GetOutcome::Value(Value::UNDEFINED))
5306 }
5307
5308 fn resolve_get_ascii(&mut self, object: Value, name: &str) -> Result<GetOutcome, EvalFailure> {
5309 debug_assert!(name.is_ascii());
5310 let slot = self.runtime_slot(object).map_err(EvalFailure::Runtime)?;
5311 let start = match slot {
5312 Some(index) => {
5313 if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5314 return match self.host.env(name).map(EcmaString::from_utf8) {
5315 Some(text) => self
5316 .allocate(HeapEntry::String(text))
5317 .map(GetOutcome::Value)
5318 .map_err(EvalFailure::Runtime),
5319 None => Ok(GetOutcome::Value(Value::UNDEFINED)),
5320 };
5321 }
5322 if let HeapEntry::String(text) = &self.heap[index] {
5323 if name == "length" {
5324 return Ok(GetOutcome::Value(number_value(text.len_units() as f64)));
5325 }
5326 if let Some(offset) = array_index_ascii(name)
5327 && let Some(unit) = text.unit_at(offset as usize)
5328 {
5329 return Ok(GetOutcome::Text(EcmaString::from_units(&[unit])));
5330 }
5331 }
5332 match self.heap[index] {
5333 HeapEntry::String(_) => self
5334 .runtime_slot(self.intrinsics.string_prototype)
5335 .map_err(EvalFailure::Runtime)?,
5336 HeapEntry::BigInt(_) | HeapEntry::PrivateName { .. } => self
5337 .runtime_slot(self.intrinsics.object_prototype)
5338 .map_err(EvalFailure::Runtime)?,
5339 HeapEntry::Symbol { .. } => self
5340 .runtime_slot(self.intrinsics.builtins.symbol_prototype())
5341 .map_err(EvalFailure::Runtime)?,
5342 _ => Some(index),
5343 }
5344 }
5345 None => {
5346 let prototype = match object.decode() {
5347 Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
5348 Some(Decoded::Number(_) | Decoded::Int32(_)) => {
5349 self.intrinsics.number_prototype
5350 }
5351 _ => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5352 };
5353 self.runtime_slot(prototype).map_err(EvalFailure::Runtime)?
5354 }
5355 };
5356 let Some(mut node) = start else {
5357 return Ok(GetOutcome::Value(Value::UNDEFINED));
5358 };
5359 for _ in 0..=self.heap.len() {
5360 if let Some(found) = self.own_get_ascii(node, name) {
5361 return self.found_outcome(found);
5362 }
5363 match self.prototype_index(node)? {
5364 Some(next) => node = next,
5365 None => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5366 }
5367 }
5368 Ok(GetOutcome::Value(Value::UNDEFINED))
5369 }
5370
5371 fn found_outcome(&mut self, found: Found) -> Result<GetOutcome, EvalFailure> {
5372 match found {
5373 Found::Value(Value::UNINITIALIZED) => {
5374 let id = self
5375 .intrinsics
5376 .builtins
5377 .id_named("ReferenceError")
5378 .expect("ReferenceError intrinsic is installed");
5379 match self.throw_error(
5380 id,
5381 "Cannot access lexical binding before initialization".into(),
5382 ) {
5383 EvalFailure::ThrowValue(value) => Err(EvalFailure::ThrowValueOrigin {
5384 value,
5385 origin: ThrowOrigin::ReferenceError {
5386 operation: "lexical binding is uninitialized",
5387 },
5388 }),
5389 failure => Err(failure),
5390 }
5391 }
5392 Found::Value(value) => Ok(GetOutcome::Value(value)),
5393 Found::Text(text) => Ok(GetOutcome::Text(text)),
5394 Found::Getter(getter) => Ok(GetOutcome::Getter(getter)),
5395 Found::Failure(kind) => Err(EvalFailure::Runtime(kind)),
5396 Found::NoGetter => Ok(GetOutcome::Value(Value::UNDEFINED)),
5397 }
5398 }
5399
5400 fn primitive_get(&self, index: usize, key: &PropertyKey) -> Option<Found> {
5401 if let HeapEntry::String(text) = &self.heap[index]
5402 && let PropertyKey::Named(name) = key
5403 {
5404 if name.eq_ascii("length") {
5405 return Some(Found::Value(number_value(text.len_units() as f64)));
5406 }
5407 if let Some(offset) = array_index(name)
5408 && let Some(unit) = text.unit_at(offset as usize)
5409 {
5410 return Some(Found::Text(EcmaString::from_units(&[unit])));
5411 }
5412 }
5413 None
5414 }
5415 fn own_get_ascii(&self, index: usize, name: &str) -> Option<Found> {
5416 debug_assert!(name.is_ascii());
5417 let slot = |value| self.runtime_slot(value).ok().flatten();
5418 if slot(self.intrinsics.object_prototype) == Some(index) && name == "toString" {
5419 return Some(Found::Value(self.intrinsics.object_to_string()));
5420 }
5421 match &self.heap[index] {
5422 HeapEntry::Object { properties, .. }
5423 | HeapEntry::Generator { properties, .. }
5424 | HeapEntry::Script { properties, .. }
5425 | HeapEntry::NativeFunction { properties, .. }
5426 | HeapEntry::Date { properties, .. }
5427 | HeapEntry::BuiltinIterator { properties, .. }
5428 | HeapEntry::Collection { properties, .. }
5429 | HeapEntry::Promise { properties, .. }
5430 | HeapEntry::Timeout { properties, .. } => property_lookup_ascii(properties, name),
5431 HeapEntry::Array {
5432 elements,
5433 properties,
5434 ..
5435 } => {
5436 if name == "length" {
5437 return Some(Found::Value(number_value(elements.len() as f64)));
5438 }
5439 if let Some(offset) = array_index_ascii(name)
5440 && let Some(element) = elements.get(offset as usize)
5441 && *element != Value::HOLE
5442 {
5443 return Some(Found::Value(*element));
5444 }
5445 property_lookup_ascii(properties, name)
5446 }
5447 HeapEntry::Function {
5448 module,
5449 function,
5450 properties,
5451 ..
5452 } => {
5453 if let Some(found) = property_lookup_ascii(properties, name) {
5454 return Some(found);
5455 }
5456 let metadata = &self.module_code(*module).functions()[function.get() as usize];
5457 if name == "length" {
5458 return Some(Found::Value(
5459 number_value(metadata.parameter_count() as f64),
5460 ));
5461 }
5462 if name == "name" {
5463 return Some(Found::Text(
5464 metadata
5465 .name()
5466 .map(|id| self.constant_text(*module, id).clone())
5467 .unwrap_or_default(),
5468 ));
5469 }
5470 None
5471 }
5472 HeapEntry::ModuleNamespace { module } => {
5473 let key = self
5474 .program_module(*module)
5475 .exports
5476 .iter()
5477 .map(|export| self.constant_text(*module, export.name))
5478 .find(|candidate| candidate.eq_ascii(name))?
5479 .clone();
5480 match self.namespace_export(*module, &key) {
5481 Ok(Some(value)) => Some(Found::Value(value)),
5482 Ok(None) => None,
5483 Err(kind) => Some(Found::Failure(kind)),
5484 }
5485 }
5486 HeapEntry::ExternalModuleNamespace { specifier } => {
5487 let export = self.registry.external[specifier]
5488 .exports
5489 .iter()
5490 .find_map(|(candidate, export)| candidate.eq_ascii(name).then_some(export))?;
5491 let cell = export
5492 .cell
5493 .expect("external namespace exports link before evaluation");
5494 Some(Found::Value(self.registry.cells[cell.0].value))
5495 }
5496 HeapEntry::RegExp {
5497 pattern,
5498 flags,
5499 properties,
5500 ..
5501 } => {
5502 if let Some(found) = property_lookup_ascii(properties, name) {
5503 return Some(found);
5504 }
5505 let flag = |unit| {
5506 Found::Value(Value::boolean(flags.as_units().contains(&u16::from(unit))))
5507 };
5508 match name {
5509 "source" => Some(Found::Text(crate::intrinsics::builtins::canonical_source(
5510 pattern,
5511 ))),
5512 "flags" => Some(Found::Text(flags.clone())),
5513 "global" => Some(flag(b'g')),
5514 "ignoreCase" => Some(flag(b'i')),
5515 "multiline" => Some(flag(b'm')),
5516 "sticky" => Some(flag(b'y')),
5517 "unicode" => Some(flag(b'u')),
5518 "dotAll" => Some(flag(b's')),
5519 "lastIndex" => Some(Found::Value(Value::int32(0))),
5520 _ => None,
5521 }
5522 }
5523 HeapEntry::HashState { update, digest, .. } => match name {
5524 "update" => Some(Found::Value(*update)),
5525 "digest" => Some(Found::Value(*digest)),
5526 _ => None,
5527 },
5528 HeapEntry::ProcessEnv { .. }
5529 | HeapEntry::String(_)
5530 | HeapEntry::BigInt(_)
5531 | HeapEntry::Symbol { .. }
5532 | HeapEntry::PrivateName { .. }
5533 | HeapEntry::Iterator { .. }
5534 | HeapEntry::PromiseResolver { .. }
5535 | HeapEntry::PromiseFinally { .. }
5536 | HeapEntry::PromiseAll { .. }
5537 | HeapEntry::AsyncActivation { .. }
5538 | HeapEntry::PromiseAllElement { .. } => None,
5539 }
5540 }
5541
5542 fn own_get(&self, index: usize, key: &PropertyKey) -> Option<Found> {
5545 if let PropertyKey::Named(name) = key {
5546 let slot = |value| self.runtime_slot(value).ok().flatten();
5547 if slot(self.intrinsics.object_prototype) == Some(index) && name.eq_ascii("toString") {
5548 return Some(Found::Value(self.intrinsics.object_to_string()));
5549 }
5550 }
5551 match &self.heap[index] {
5552 HeapEntry::Object { properties, .. }
5553 | HeapEntry::Generator { properties, .. }
5554 | HeapEntry::Script { properties, .. }
5555 | HeapEntry::Date { properties, .. }
5556 | HeapEntry::BuiltinIterator { properties, .. }
5557 | HeapEntry::Collection { properties, .. }
5558 | HeapEntry::Promise { properties, .. }
5559 | HeapEntry::Timeout { properties, .. } => property_lookup(properties, key),
5560 HeapEntry::Array {
5561 elements,
5562 properties,
5563 ..
5564 } => {
5565 if let PropertyKey::Named(name) = key {
5566 if name.eq_ascii("length") {
5567 return Some(Found::Value(number_value(elements.len() as f64)));
5568 }
5569 if let Some(offset) = array_index(name)
5570 && let Some(element) = elements.get(offset as usize)
5571 && *element != Value::HOLE
5572 {
5573 return Some(Found::Value(*element));
5574 }
5575 }
5576 property_lookup(properties, key)
5577 }
5578 HeapEntry::Function {
5579 module,
5580 function,
5581 properties,
5582 ..
5583 } => {
5584 if let Some(found) = property_lookup(properties, key) {
5585 return Some(found);
5586 }
5587 if let PropertyKey::Named(name) = key {
5588 let metadata = &self.module_code(*module).functions()[function.get() as usize];
5589 if name.eq_ascii("length") {
5590 return Some(Found::Value(
5591 number_value(metadata.parameter_count() as f64),
5592 ));
5593 }
5594 if name.eq_ascii("name") {
5595 return Some(Found::Text(
5596 metadata
5597 .name()
5598 .map(|id| self.constant_text(*module, id).clone())
5599 .unwrap_or_default(),
5600 ));
5601 }
5602 }
5603 None
5604 }
5605 HeapEntry::ModuleNamespace { module } => {
5606 let PropertyKey::Named(name) = key else {
5607 return None;
5608 };
5609 match self.namespace_export(*module, name) {
5610 Ok(Some(value)) => Some(Found::Value(value)),
5611 Ok(None) => None,
5612 Err(kind) => Some(Found::Failure(kind)),
5613 }
5614 }
5615 HeapEntry::ExternalModuleNamespace { specifier } => {
5616 let PropertyKey::Named(name) = key else {
5617 return None;
5618 };
5619 let export = self.registry.external[specifier].exports.get(name)?;
5620 Some(Found::Value(export.cell.map_or(export.value, |cell| {
5621 self.registry.cells[cell.0].value
5622 })))
5623 }
5624 HeapEntry::NativeFunction { properties, .. } => property_lookup(properties, key),
5625 HeapEntry::RegExp {
5626 pattern,
5627 flags,
5628 properties,
5629 ..
5630 } => {
5631 if let Some(found) = property_lookup(properties, key) {
5632 return Some(found);
5633 }
5634 if let PropertyKey::Named(name) = key {
5635 let flag = |ascii: &str| {
5636 Found::Value(Value::boolean(
5637 flags.as_units().contains(&u16::from(ascii.as_bytes()[0])),
5638 ))
5639 };
5640 if name.eq_ascii("source") {
5641 return Some(Found::Text(crate::intrinsics::builtins::canonical_source(
5642 pattern,
5643 )));
5644 }
5645 if name.eq_ascii("flags") {
5646 return Some(Found::Text(flags.clone()));
5647 }
5648 if name.eq_ascii("global") {
5649 return Some(flag("g"));
5650 }
5651 if name.eq_ascii("ignoreCase") {
5652 return Some(flag("i"));
5653 }
5654 if name.eq_ascii("multiline") {
5655 return Some(flag("m"));
5656 }
5657 if name.eq_ascii("sticky") {
5658 return Some(flag("y"));
5659 }
5660 if name.eq_ascii("unicode") {
5661 return Some(flag("u"));
5662 }
5663 if name.eq_ascii("dotAll") {
5664 return Some(flag("s"));
5665 }
5666 if name.eq_ascii("lastIndex") {
5667 return Some(Found::Value(Value::int32(0)));
5668 }
5669 }
5670 None
5671 }
5672 HeapEntry::HashState { update, digest, .. } => {
5673 let PropertyKey::Named(name) = key else {
5674 return None;
5675 };
5676 if name.eq_ascii("update") {
5677 Some(Found::Value(*update))
5678 } else if name.eq_ascii("digest") {
5679 Some(Found::Value(*digest))
5680 } else {
5681 None
5682 }
5683 }
5684 HeapEntry::ProcessEnv { .. }
5685 | HeapEntry::String(_)
5686 | HeapEntry::BigInt(_)
5687 | HeapEntry::Symbol { .. }
5688 | HeapEntry::PrivateName { .. }
5689 | HeapEntry::Iterator { .. }
5690 | HeapEntry::PromiseResolver { .. }
5691 | HeapEntry::PromiseFinally { .. }
5692 | HeapEntry::PromiseAll { .. }
5693 | HeapEntry::AsyncActivation { .. }
5694 | HeapEntry::PromiseAllElement { .. } => None,
5695 }
5696 }
5697
5698 fn namespace_export(
5699 &self,
5700 module: ModuleId,
5701 name: &EcmaString,
5702 ) -> Result<Option<Value>, RuntimeErrorKind> {
5703 if module.get() as usize >= self.dynamic_base {
5704 return Ok(None);
5705 }
5706 match self.program().resolve_export(module, name) {
5707 Some(ResolvedExport::Local { module, binding }) => {
5708 let cell = self.registry.modules[module.get() as usize].binding_cells
5709 [binding.get() as usize]
5710 .expect("verified export resolves to a linked cell");
5711 let value = self.registry.cells[cell.0].value;
5712 if value.is_uninitialized() {
5713 Err(RuntimeErrorKind::TemporalDeadZone { module, binding })
5714 } else {
5715 Ok(Some(value))
5716 }
5717 }
5718 Some(ResolvedExport::External { module, edge, name }) => {
5719 let Some(specifier) = self.external_specifier(module, edge) else {
5720 return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5721 };
5722 let name = self.constant_text(module, name);
5723 let Some(export) = self.registry.external[&specifier].exports.get(name) else {
5724 return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5725 };
5726 let Some(cell) = export.cell else {
5727 return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5728 };
5729 Ok(Some(self.registry.cells[cell.0].value))
5730 }
5731 None => Ok(None),
5732 }
5733 }
5734
5735 fn own_data_property(&self, index: usize, name: &str) -> Option<Value> {
5736 let properties = match &self.heap[index] {
5737 HeapEntry::Object { properties, .. }
5738 | HeapEntry::Generator { properties, .. }
5739 | HeapEntry::Script { properties, .. }
5740 | HeapEntry::Array { properties, .. }
5741 | HeapEntry::Function { properties, .. }
5742 | HeapEntry::NativeFunction { properties, .. }
5743 | HeapEntry::RegExp { properties, .. }
5744 | HeapEntry::Date { properties, .. }
5745 | HeapEntry::BuiltinIterator { properties, .. }
5746 | HeapEntry::Collection { properties, .. }
5747 | HeapEntry::Promise { properties, .. }
5748 | HeapEntry::Timeout { properties, .. } => properties,
5749 _ => return None,
5750 };
5751 match properties.get_ascii(name) {
5752 Some(Property::Data { value, .. }) => Some(*value),
5753 _ => None,
5754 }
5755 }
5756
5757 fn prototype_index(&self, index: usize) -> Result<Option<usize>, EvalFailure> {
5758 let prototype = match &self.heap[index] {
5759 HeapEntry::Object { prototype, .. }
5760 | HeapEntry::Generator { prototype, .. }
5761 | HeapEntry::Script { prototype, .. }
5762 | HeapEntry::Array { prototype, .. }
5763 | HeapEntry::Function { prototype, .. }
5764 | HeapEntry::RegExp { prototype, .. }
5765 | HeapEntry::Date { prototype, .. }
5766 | HeapEntry::BuiltinIterator { prototype, .. }
5767 | HeapEntry::Collection { prototype, .. }
5768 | HeapEntry::Promise { prototype, .. }
5769 | HeapEntry::Timeout { prototype, .. }
5770 | HeapEntry::ProcessEnv { prototype, .. } => *prototype,
5771 HeapEntry::NativeFunction { .. } => Some(self.intrinsics.function_prototype),
5772 _ => None,
5773 };
5774 match prototype {
5775 Some(value) => self.runtime_slot(value).map_err(EvalFailure::Runtime),
5776 None => Ok(None),
5777 }
5778 }
5779
5780 pub(crate) fn inherits_from_prototype(
5781 &self,
5782 value: Value,
5783 prototype: Value,
5784 ) -> Result<bool, EvalFailure> {
5785 let Some(mut current) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
5786 return Ok(false);
5787 };
5788 let Some(target) = self.runtime_slot(prototype).map_err(EvalFailure::Runtime)? else {
5789 return Ok(false);
5790 };
5791 let mut traversed = 0;
5792 while let Some(next) = self.prototype_index(current)? {
5793 if next == target {
5794 return Ok(true);
5795 }
5796 current = next;
5797 traversed += 1;
5798 if traversed > self.heap.len() {
5799 return Ok(false);
5800 }
5801 }
5802 Ok(false)
5803 }
5804
5805 fn resolve_set(
5808 &mut self,
5809 object: Value,
5810 key: PropertyKey,
5811 value: Value,
5812 ) -> Result<SetOutcome, EvalFailure> {
5813 match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
5814 Some(index) => {
5815 if matches!(self.heap[index], HeapEntry::ModuleNamespace { .. }) {
5816 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5817 operation: "assign to module namespace",
5818 }));
5819 }
5820 if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5821 let PropertyKey::Named(name) = &key else {
5822 return Ok(SetOutcome::Done);
5823 };
5824 let Ok(name) = name.to_utf8_strict() else {
5825 return Ok(SetOutcome::Done);
5826 };
5827 let text = self.to_string(value)?;
5828 let text = crate::host_objects::env_value_text_lossy(&text);
5829 self.host.set_env(&name, &text);
5830 return Ok(SetOutcome::Done);
5831 }
5832 if let Some(setter) = self.find_setter(index, &key)? {
5833 return Ok(match setter {
5834 Some(setter) => SetOutcome::Setter(setter),
5835 None => SetOutcome::Done,
5836 });
5837 }
5838 self.set_own_data(index, key, value)?;
5839 Ok(SetOutcome::Done)
5840 }
5841 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5842 operation: "set property on primitive",
5843 })),
5844 }
5845 }
5846
5847 fn find_setter(
5848 &self,
5849 index: usize,
5850 key: &PropertyKey,
5851 ) -> Result<Option<Option<Value>>, EvalFailure> {
5852 if self.own_has_non_accessor(index, key) {
5853 return Ok(None);
5854 }
5855 let mut node = index;
5856 let mut guard = 0;
5857 loop {
5858 let accessor = match &self.heap[node] {
5859 HeapEntry::Object { properties, .. }
5860 | HeapEntry::Generator { properties, .. }
5861 | HeapEntry::Script { properties, .. }
5862 | HeapEntry::Array { properties, .. }
5863 | HeapEntry::Function { properties, .. }
5864 | HeapEntry::NativeFunction { properties, .. }
5865 | HeapEntry::RegExp { properties, .. }
5866 | HeapEntry::Date { properties, .. }
5867 | HeapEntry::BuiltinIterator { properties, .. }
5868 | HeapEntry::Collection { properties, .. }
5869 | HeapEntry::Promise { properties, .. }
5870 | HeapEntry::Timeout { properties, .. } => match properties.get(key) {
5871 Some(Property::Accessor { setter, .. }) => Some(Some(*setter)),
5872 Some(Property::Data { .. }) => Some(None),
5873 None => None,
5874 },
5875 _ => None,
5876 };
5877 match accessor {
5878 Some(Some(setter)) => return Ok(Some(setter)),
5879 Some(None) => return Ok(None),
5880 None => {}
5881 }
5882 match self.prototype_index(node)? {
5883 Some(next) => {
5884 node = next;
5885 guard += 1;
5886 if guard > self.heap.len() + 1 {
5887 return Ok(None);
5888 }
5889 }
5890 None => return Ok(None),
5891 }
5892 }
5893 }
5894
5895 fn own_has_non_accessor(&self, index: usize, key: &PropertyKey) -> bool {
5896 match &self.heap[index] {
5897 HeapEntry::Array { elements, .. } => {
5898 if let PropertyKey::Named(name) = key {
5899 if name.eq_ascii("length") {
5900 return true;
5901 }
5902 if let Some(offset) = array_index(name) {
5903 return elements
5904 .get(offset as usize)
5905 .is_some_and(|element| *element != Value::HOLE);
5906 }
5907 }
5908 false
5909 }
5910 HeapEntry::Function { .. } => {
5911 (key.eq_ascii("length") || key.eq_ascii("name"))
5912 && match key {
5913 PropertyKey::Named(name) if name.eq_ascii("length") => {
5914 self.own_data_property(index, "length").is_none()
5915 }
5916 PropertyKey::Named(_) => self.own_data_property(index, "name").is_none(),
5917 _ => false,
5918 }
5919 }
5920 _ => false,
5921 }
5922 }
5923
5924 fn set_own_data(
5925 &mut self,
5926 index: usize,
5927 key: PropertyKey,
5928 value: Value,
5929 ) -> Result<(), EvalFailure> {
5930 if matches!(key, PropertyKey::Named(ref name) if name.eq_ascii("length"))
5931 && matches!(self.heap[index], HeapEntry::Array { .. })
5932 {
5933 let HeapEntry::Array {
5934 elements,
5935 properties,
5936 length_writable,
5937 ..
5938 } = &mut self.heap[index]
5939 else {
5940 unreachable!("array checked above");
5941 };
5942 return array_set_length(
5943 elements,
5944 properties,
5945 *length_writable,
5946 value,
5947 "set array length",
5948 );
5949 }
5950 if let HeapEntry::Array {
5951 elements,
5952 length_writable,
5953 ..
5954 } = &self.heap[index]
5955 && let Some(offset) = key.as_string().and_then(array_index)
5956 && offset as usize >= elements.len()
5957 && !*length_writable
5958 {
5959 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5960 operation: "add index beyond non-writable array length",
5961 }));
5962 }
5963 let (properties, extensible, virtual_exists) = match &self.heap[index] {
5964 HeapEntry::Object {
5965 properties,
5966 extensible,
5967 ..
5968 }
5969 | HeapEntry::Generator {
5970 properties,
5971 extensible,
5972 ..
5973 }
5974 | HeapEntry::Script {
5975 properties,
5976 extensible,
5977 ..
5978 }
5979 | HeapEntry::Function {
5980 properties,
5981 extensible,
5982 ..
5983 }
5984 | HeapEntry::NativeFunction {
5985 properties,
5986 extensible,
5987 ..
5988 }
5989 | HeapEntry::RegExp {
5990 properties,
5991 extensible,
5992 ..
5993 }
5994 | HeapEntry::Date {
5995 properties,
5996 extensible,
5997 ..
5998 }
5999 | HeapEntry::BuiltinIterator {
6000 properties,
6001 extensible,
6002 ..
6003 }
6004 | HeapEntry::Collection {
6005 properties,
6006 extensible,
6007 ..
6008 }
6009 | HeapEntry::Promise {
6010 properties,
6011 extensible,
6012 ..
6013 } => (Some(properties), *extensible, false),
6014 HeapEntry::Array {
6015 elements,
6016 properties,
6017 extensible,
6018 ..
6019 } => {
6020 let virtual_exists = key.as_string().is_some_and(|name| {
6021 name.eq_ascii("length")
6022 || array_index(name).is_some_and(|offset| {
6023 elements
6024 .get(offset as usize)
6025 .is_some_and(|element| *element != Value::HOLE)
6026 })
6027 });
6028 (Some(properties), *extensible, virtual_exists)
6029 }
6030 _ => (None, true, false),
6031 };
6032 if let Some(property) = properties.and_then(|properties| properties.get(&key)) {
6033 match property {
6034 Property::Data {
6035 writable: false, ..
6036 }
6037 | Property::Accessor { .. } => {
6038 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6039 operation: "assign to read only property",
6040 }));
6041 }
6042 Property::Data { writable: true, .. } => {}
6043 }
6044 } else if !extensible && !virtual_exists {
6045 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6046 operation: "add property to non-extensible object",
6047 }));
6048 }
6049
6050 let growth = match &self.heap[index] {
6051 HeapEntry::Object { properties, .. }
6052 | HeapEntry::Generator { properties, .. }
6053 | HeapEntry::Script { properties, .. }
6054 | HeapEntry::Function { properties, .. }
6055 | HeapEntry::NativeFunction { properties, .. }
6056 | HeapEntry::RegExp { properties, .. }
6057 | HeapEntry::Date { properties, .. }
6058 | HeapEntry::BuiltinIterator { properties, .. }
6059 | HeapEntry::Collection { properties, .. }
6060 | HeapEntry::Promise { properties, .. }
6061 | HeapEntry::Timeout { properties, .. } => {
6062 usize::from(!properties.contains_key(&key)) * key.charge_bytes()
6063 }
6064 HeapEntry::Array {
6065 elements,
6066 properties,
6067 ..
6068 } => match &key {
6069 PropertyKey::Named(name) if name.eq_ascii("length") => 0,
6070 PropertyKey::Named(name) => {
6071 if let Some(offset) = array_index(name) {
6072 (offset as usize + 1).saturating_sub(elements.len()) * 8
6073 } else {
6074 usize::from(!properties.contains_key(&key)) * key.charge_bytes()
6075 }
6076 }
6077 PropertyKey::Symbol(_) | PropertyKey::Private(_) => {
6078 usize::from(!properties.contains_key(&key)) * key.charge_bytes()
6079 }
6080 },
6081 HeapEntry::String(_) | HeapEntry::BigInt(_) => {
6082 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6083 operation: "set property on primitive",
6084 }));
6085 }
6086 HeapEntry::Symbol { .. }
6087 | HeapEntry::PrivateName { .. }
6088 | HeapEntry::Iterator { .. }
6089 | HeapEntry::PromiseResolver { .. }
6090 | HeapEntry::PromiseFinally { .. }
6091 | HeapEntry::PromiseAll { .. }
6092 | HeapEntry::AsyncActivation { .. }
6093 | HeapEntry::PromiseAllElement { .. } => {
6094 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6095 operation: "set property on non-object",
6096 }));
6097 }
6098 HeapEntry::ProcessEnv { .. } => {
6099 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6100 operation: "set internal process environment",
6101 }));
6102 }
6103 HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
6104 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6105 operation: "assign to module namespace",
6106 }));
6107 }
6108 HeapEntry::HashState { .. } => {
6109 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6110 operation: "assign to hash state",
6111 }));
6112 }
6113 };
6114 self.charge_heap(growth).map_err(EvalFailure::Runtime)?;
6115 match &mut self.heap[index] {
6116 HeapEntry::Object { properties, .. }
6117 | HeapEntry::Generator { properties, .. }
6118 | HeapEntry::Script { properties, .. }
6119 | HeapEntry::Function { properties, .. }
6120 | HeapEntry::NativeFunction { properties, .. }
6121 | HeapEntry::RegExp { properties, .. }
6122 | HeapEntry::Date { properties, .. }
6123 | HeapEntry::BuiltinIterator { properties, .. }
6124 | HeapEntry::Collection { properties, .. }
6125 | HeapEntry::Promise { properties, .. }
6126 | HeapEntry::Timeout { properties, .. } => {
6127 properties.insert(
6128 key,
6129 Property::Data {
6130 value,
6131 writable: true,
6132 enumerable: true,
6133 configurable: true,
6134 },
6135 );
6136 Ok(())
6137 }
6138 HeapEntry::Array {
6139 elements,
6140 properties,
6141 length_writable,
6142 ..
6143 } => {
6144 match key {
6145 PropertyKey::Named(name) => {
6146 if let Some(offset) = array_index(&name) {
6147 let offset = offset as usize;
6148 if elements.len() <= offset {
6149 array_set_length(
6150 elements,
6151 properties,
6152 *length_writable,
6153 number_value((offset + 1) as f64),
6154 "set array index",
6155 )?;
6156 }
6157 elements[offset] = value;
6158 } else {
6159 properties.insert(
6160 PropertyKey::Named(name),
6161 Property::Data {
6162 value,
6163 writable: true,
6164 enumerable: true,
6165 configurable: true,
6166 },
6167 );
6168 }
6169 }
6170 identity @ (PropertyKey::Symbol(_) | PropertyKey::Private(_)) => {
6171 properties.insert(
6172 identity,
6173 Property::Data {
6174 value,
6175 writable: true,
6176 enumerable: true,
6177 configurable: true,
6178 },
6179 );
6180 }
6181 }
6182 Ok(())
6183 }
6184 HeapEntry::ProcessEnv { .. } => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6185 operation: "set internal process environment",
6186 })),
6187 _ => unreachable!("primitive and identity entries rejected above"),
6188 }
6189 }
6190
6191 fn define_accessor(
6192 &mut self,
6193 object: Value,
6194 key: PropertyKey,
6195 accessor: Value,
6196 kind: AccessorKind,
6197 ) -> Result<(), EvalFailure> {
6198 match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6199 Some(index) => {
6200 self.charge_heap(key.charge_bytes() + 8)
6201 .map_err(EvalFailure::Runtime)?;
6202 let (properties, extensible) = match &mut self.heap[index] {
6203 HeapEntry::Object {
6204 properties,
6205 extensible,
6206 ..
6207 }
6208 | HeapEntry::Generator {
6209 properties,
6210 extensible,
6211 ..
6212 }
6213 | HeapEntry::Script {
6214 properties,
6215 extensible,
6216 ..
6217 }
6218 | HeapEntry::Array {
6219 properties,
6220 extensible,
6221 ..
6222 }
6223 | HeapEntry::Function {
6224 properties,
6225 extensible,
6226 ..
6227 }
6228 | HeapEntry::NativeFunction {
6229 properties,
6230 extensible,
6231 ..
6232 }
6233 | HeapEntry::RegExp {
6234 properties,
6235 extensible,
6236 ..
6237 }
6238 | HeapEntry::Date {
6239 properties,
6240 extensible,
6241 ..
6242 }
6243 | HeapEntry::BuiltinIterator {
6244 properties,
6245 extensible,
6246 ..
6247 }
6248 | HeapEntry::Collection {
6249 properties,
6250 extensible,
6251 ..
6252 }
6253 | HeapEntry::Promise {
6254 properties,
6255 extensible,
6256 ..
6257 } => (properties, *extensible),
6258 _ => {
6259 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6260 operation: "define accessor on primitive",
6261 }));
6262 }
6263 };
6264 if properties
6265 .get(&key)
6266 .is_some_and(|property| !property.configurable())
6267 || (!properties.contains_key(&key) && !extensible)
6268 {
6269 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6270 operation: "define accessor on non-configurable object",
6271 }));
6272 }
6273 let property = properties.get_mut(&key);
6274 match property {
6275 Some(Property::Accessor { getter, setter, .. }) => match kind {
6276 AccessorKind::Getter => *getter = Some(accessor),
6277 AccessorKind::Setter => *setter = Some(accessor),
6278 },
6279 Some(Property::Data { .. }) | None => {
6280 let (getter, setter) = match kind {
6281 AccessorKind::Getter => (Some(accessor), None),
6282 AccessorKind::Setter => (None, Some(accessor)),
6283 };
6284 properties.insert(
6285 key,
6286 Property::Accessor {
6287 getter,
6288 setter,
6289 enumerable: true,
6290 configurable: true,
6291 },
6292 );
6293 }
6294 }
6295 Ok(())
6296 }
6297 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6298 operation: "define accessor on host object",
6299 })),
6300 }
6301 }
6302
6303 fn delete_property(&mut self, object: Value, key: &PropertyKey) -> Result<bool, EvalFailure> {
6304 match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6305 Some(index) => match &mut self.heap[index] {
6306 HeapEntry::Object { properties, .. }
6307 | HeapEntry::Generator { properties, .. }
6308 | HeapEntry::Script { properties, .. }
6309 | HeapEntry::Function { properties, .. }
6310 | HeapEntry::NativeFunction { properties, .. }
6311 | HeapEntry::RegExp { properties, .. }
6312 | HeapEntry::Date { properties, .. }
6313 | HeapEntry::BuiltinIterator { properties, .. }
6314 | HeapEntry::Collection { properties, .. }
6315 | HeapEntry::Promise { properties, .. }
6316 | HeapEntry::Timeout { properties, .. } => {
6317 if properties
6318 .get(key)
6319 .is_some_and(|property| !property.configurable())
6320 {
6321 return Ok(false);
6322 }
6323 properties.remove(key);
6324 Ok(true)
6325 }
6326 HeapEntry::Array {
6327 elements,
6328 properties,
6329 ..
6330 } => {
6331 if properties
6332 .get(key)
6333 .is_some_and(|property| !property.configurable())
6334 {
6335 return Ok(false);
6336 }
6337 if properties.remove(key).is_some() {
6338 return Ok(true);
6339 }
6340 if let PropertyKey::Named(name) = key {
6341 if name.eq_ascii("length") {
6342 return Ok(false);
6343 }
6344 if let Some(offset) = array_index(name) {
6345 if let Some(element) = elements.get_mut(offset as usize) {
6346 *element = Value::HOLE;
6347 }
6348 return Ok(true);
6349 }
6350 }
6351 Ok(true)
6352 }
6353 HeapEntry::ProcessEnv { .. } => {
6354 let PropertyKey::Named(name) = key else {
6355 return Ok(true);
6356 };
6357 Ok(name
6358 .to_utf8_strict()
6359 .is_ok_and(|name| self.host.delete_env(&name)))
6360 }
6361 HeapEntry::String(_)
6362 | HeapEntry::BigInt(_)
6363 | HeapEntry::Symbol { .. }
6364 | HeapEntry::PrivateName { .. }
6365 | HeapEntry::Iterator { .. }
6366 | HeapEntry::PromiseResolver { .. }
6367 | HeapEntry::PromiseFinally { .. }
6368 | HeapEntry::PromiseAll { .. }
6369 | HeapEntry::AsyncActivation { .. }
6370 | HeapEntry::PromiseAllElement { .. }
6371 | HeapEntry::HashState { .. } => Ok(true),
6372 HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
6373 Ok(false)
6374 }
6375 },
6376 None => Ok(true),
6377 }
6378 }
6379
6380 fn has_property(&mut self, object: Value, key: &PropertyKey) -> Result<bool, EvalFailure> {
6381 match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6382 Some(index) => {
6383 if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
6384 let PropertyKey::Named(name) = key else {
6385 return Ok(false);
6386 };
6387 return Ok(name
6388 .to_utf8_strict()
6389 .is_ok_and(|name| self.host.env(&name).is_some()));
6390 }
6391 if matches!(key, PropertyKey::Private(_)) {
6392 return Ok(self.own_get(index, key).is_some());
6393 }
6394 let mut node = index;
6395 let mut guard = 0;
6396 loop {
6397 if self.own_get(node, key).is_some() {
6398 return Ok(true);
6399 }
6400 match self.prototype_index(node)? {
6401 Some(next) => {
6402 node = next;
6403 guard += 1;
6404 if guard > self.heap.len() + 1 {
6405 return Ok(false);
6406 }
6407 }
6408 None => return Ok(false),
6409 }
6410 }
6411 }
6412 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6413 operation: "in",
6414 })),
6415 }
6416 }
6417
6418 pub(crate) fn array_push(&mut self, array: Value, value: Value) -> Result<(), EvalFailure> {
6421 match self.runtime_slot(array).map_err(EvalFailure::Runtime)? {
6422 Some(index) => {
6423 if !matches!(self.heap[index], HeapEntry::Array { .. }) {
6424 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6425 operation: "push on non-array",
6426 }));
6427 }
6428 self.charge_heap(8).map_err(EvalFailure::Runtime)?;
6429 if let HeapEntry::Array {
6430 elements,
6431 properties,
6432 length_writable,
6433 ..
6434 } = &mut self.heap[index]
6435 {
6436 let offset = elements.len();
6437 array_set_length(
6438 elements,
6439 properties,
6440 *length_writable,
6441 number_value((offset + 1) as f64),
6442 "push beyond non-writable array length",
6443 )?;
6444 elements[offset] = value;
6445 }
6446 Ok(())
6447 }
6448 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6449 operation: "push on non-array",
6450 })),
6451 }
6452 }
6453
6454 fn array_extend(&mut self, array: Value, iterable: Value) -> Result<(), EvalFailure> {
6455 let iterator = self.create_iterator(iterable, IteratorKind::Sync)?;
6456 loop {
6457 let (done, value) = self.iterator_next(iterator)?;
6458 if done {
6459 return Ok(());
6460 }
6461 self.array_push(array, value)?;
6462 }
6463 }
6464
6465 fn object_spread(&mut self, target: Value, source: Value) -> Result<(), EvalFailure> {
6466 let target_index = match self.runtime_slot(target).map_err(EvalFailure::Runtime)? {
6467 Some(index)
6468 if matches!(
6469 self.heap[index],
6470 HeapEntry::Object { .. }
6471 | HeapEntry::Generator { .. }
6472 | HeapEntry::Script { .. }
6473 | HeapEntry::Array { .. }
6474 | HeapEntry::Promise { .. }
6475 ) =>
6476 {
6477 index
6478 }
6479 _ => {
6480 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6481 operation: "object spread target is not an object",
6482 }));
6483 }
6484 };
6485 let keys = self.own_property_keys(source)?;
6486 for key in keys {
6487 if !self.own_property_is_enumerable(source, &key)? {
6488 continue;
6489 }
6490 let value = self.get_property_key(source, &key)?;
6491 self.set_own_data(target_index, key, value)?;
6492 }
6493 Ok(())
6494 }
6495
6496 fn set_prototype(&mut self, object: Value, prototype: Value) -> Result<(), EvalFailure> {
6497 let prototype = match self.runtime_slot(prototype).map_err(EvalFailure::Runtime)? {
6498 Some(_) => Some(prototype),
6499 None => match prototype.decode() {
6500 Some(Decoded::Null) => None,
6501 Some(Decoded::HeapRef(_)) => Some(prototype),
6502 _ => {
6503 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6504 operation: "set prototype to non-object",
6505 }));
6506 }
6507 },
6508 };
6509 match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6510 Some(index) => match &mut self.heap[index] {
6511 HeapEntry::Object {
6512 prototype: slot, ..
6513 }
6514 | HeapEntry::Generator {
6515 prototype: slot, ..
6516 }
6517 | HeapEntry::Script {
6518 prototype: slot, ..
6519 }
6520 | HeapEntry::Array {
6521 prototype: slot, ..
6522 }
6523 | HeapEntry::Function {
6524 prototype: slot, ..
6525 }
6526 | HeapEntry::RegExp {
6527 prototype: slot, ..
6528 }
6529 | HeapEntry::Date {
6530 prototype: slot, ..
6531 }
6532 | HeapEntry::BuiltinIterator {
6533 prototype: slot, ..
6534 }
6535 | HeapEntry::Collection {
6536 prototype: slot, ..
6537 }
6538 | HeapEntry::Promise {
6539 prototype: slot, ..
6540 } => {
6541 *slot = prototype;
6542 Ok(())
6543 }
6544 _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6545 operation: "set prototype on primitive",
6546 })),
6547 },
6548 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6549 operation: "set prototype on host object",
6550 })),
6551 }
6552 }
6553
6554 pub(crate) fn create_generator(
6555 &mut self,
6556 start: GeneratorStart,
6557 ) -> Result<Value, RuntimeErrorKind> {
6558 self.allocate(HeapEntry::Generator {
6559 state: GeneratorState::SuspendedStart(start),
6560 properties: PropertyMap::default(),
6561 prototype: Some(self.intrinsics.builtins.generator_prototype()),
6562 extensible: true,
6563 })
6564 }
6565
6566 fn resume_generator(
6567 &mut self,
6568 generator: Value,
6569 resume_value: Value,
6570 ) -> Result<Value, EvalFailure> {
6571 let state = self.take_generator_state(generator)?;
6572 if matches!(&state, GeneratorState::Completed) {
6573 return self.iterator_result(Value::UNDEFINED, true);
6574 }
6575
6576 let stop_depth = self.frames.len();
6577 let return_to = self.frames.last().map(|frame| ReturnTo {
6578 destination: None,
6579 call_pc: frame.pc,
6580 constructed: None,
6581 });
6582 let prepared = match state {
6583 GeneratorState::SuspendedStart(start) => self
6584 .push_frame(
6585 start.target,
6586 &start.captures,
6587 start.this_value,
6588 start.new_target,
6589 &start.args,
6590 return_to,
6591 )
6592 .map_err(|error| EvalFailure::Runtime(error.kind)),
6593 GeneratorState::Suspended(activation) => {
6594 self.push_resumed_generator_frame(activation, resume_value, return_to)
6595 }
6596 GeneratorState::Executing | GeneratorState::Completed => unreachable!(),
6597 };
6598 if let Err(failure) = prepared {
6599 self.settle_generator_completed(generator)?;
6600 return Err(failure);
6601 }
6602
6603 let resumed = self.run_generator_activation(stop_depth);
6604 match resumed {
6605 Ok(GeneratorResume::Yield { value, activation }) => {
6606 self.settle_generator_yield(generator, value, activation)
6607 }
6608 Ok(GeneratorResume::Return(value)) => {
6609 self.settle_generator_completed(generator)?;
6610 self.iterator_result(value, true)
6611 }
6612 Ok(GeneratorResume::Throw { value, origin }) => {
6613 self.settle_generator_completed(generator)?;
6614 Err(EvalFailure::ThrowValueOrigin { value, origin })
6615 }
6616 Err(failure) => {
6617 self.settle_generator_completed(generator)?;
6618 Err(failure)
6619 }
6620 }
6621 }
6622
6623 fn push_resumed_generator_frame(
6624 &mut self,
6625 activation: SuspendedActivation,
6626 resume_value: Value,
6627 return_to: Option<ReturnTo>,
6628 ) -> Result<(), EvalFailure> {
6629 if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
6630 self.release_suspended_activation_registers(activation.registers.len());
6631 return Err(EvalFailure::Runtime(RuntimeErrorKind::CallDepthExceeded {
6632 limit: self.limits.max_call_depth,
6633 }));
6634 }
6635 let suspend_pc = activation
6636 .resume_token
6637 .checked_sub(1)
6638 .expect("suspended generator token is nonzero") as usize;
6639 let instruction = self.module_code(activation.target.module).functions()
6640 [activation.target.function.get() as usize]
6641 .code()[suspend_pc];
6642 let Instruction::Suspend { dst, resume, .. } = instruction else {
6643 unreachable!("generator resume token names a suspend instruction");
6644 };
6645 let mut frame = Frame {
6646 module: activation.target.module,
6647 function: activation.target.function.get() as usize,
6648 pc: resume.get() as usize,
6649 registers: activation.registers,
6650 return_to,
6651 this_value: activation.this_value,
6652 new_target: activation.new_target,
6653 args: activation.args,
6654 arguments_object: activation.arguments_object,
6655 };
6656 frame.registers[dst.get() as usize] = resume_value;
6657 self.frames.push(frame);
6658 Ok(())
6659 }
6660
6661 fn run_generator_activation(
6662 &mut self,
6663 stop_depth: usize,
6664 ) -> Result<GeneratorResume, EvalFailure> {
6665 self.last_completion = None;
6666 self.pending_generator_resume = None;
6667 self.callback_boundaries.push(stop_depth);
6668 self.generator_boundaries.push(stop_depth);
6669 let result = self.run_loop(stop_depth);
6670 self.generator_boundaries
6671 .pop()
6672 .expect("generator execution owns its suspend boundary");
6673 self.callback_boundaries
6674 .pop()
6675 .expect("generator execution owns its unwind boundary");
6676
6677 match result {
6678 Ok(Some(execution)) => Ok(GeneratorResume::Return(execution.value)),
6679 Ok(None) => {
6680 if let Some(resume) = self.pending_generator_resume.take() {
6681 return Ok(resume);
6682 }
6683 let value = self.last_completion.take().unwrap_or(Value::UNDEFINED);
6684 Ok(GeneratorResume::Return(value))
6685 }
6686 Err(error) => {
6687 self.unwind_frames_to(stop_depth);
6688 match error.kind {
6689 RuntimeErrorKind::UncaughtThrow { value, origin } => {
6690 Ok(GeneratorResume::Throw { value, origin })
6691 }
6692 kind => Err(EvalFailure::Runtime(kind)),
6693 }
6694 }
6695 }
6696 }
6697
6698 pub(crate) fn take_generator_state(
6699 &mut self,
6700 generator: Value,
6701 ) -> Result<GeneratorState, EvalFailure> {
6702 let Some(index) = self.runtime_slot(generator).map_err(EvalFailure::Runtime)? else {
6703 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6704 operation: "Generator.prototype.next called on incompatible receiver",
6705 }));
6706 };
6707 let HeapEntry::Generator { state, .. } = &mut self.heap[index] else {
6708 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6709 operation: "Generator.prototype.next called on incompatible receiver",
6710 }));
6711 };
6712 match std::mem::replace(state, GeneratorState::Executing) {
6713 GeneratorState::Executing => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6714 operation: "generator is already running",
6715 })),
6716 GeneratorState::Completed => {
6717 *state = GeneratorState::Completed;
6718 Ok(GeneratorState::Completed)
6719 }
6720 state => Ok(state),
6721 }
6722 }
6723
6724 pub(crate) fn settle_generator_yield(
6725 &mut self,
6726 generator: Value,
6727 value: Value,
6728 activation: SuspendedActivation,
6729 ) -> Result<Value, EvalFailure> {
6730 let register_count = activation.registers.len();
6731 let result = match self.iterator_result(value, false) {
6732 Ok(result) => result,
6733 Err(failure) => {
6734 self.release_suspended_activation_registers(register_count);
6735 self.replace_executing_generator(generator, GeneratorState::Completed)?;
6736 return Err(failure);
6737 }
6738 };
6739 if let Err(failure) =
6740 self.replace_executing_generator(generator, GeneratorState::Suspended(activation))
6741 {
6742 self.release_suspended_activation_registers(register_count);
6743 return Err(failure);
6744 }
6745 Ok(result)
6746 }
6747
6748 pub(crate) fn settle_generator_completed(
6749 &mut self,
6750 generator: Value,
6751 ) -> Result<(), EvalFailure> {
6752 self.replace_executing_generator(generator, GeneratorState::Completed)
6753 }
6754
6755 fn replace_executing_generator(
6756 &mut self,
6757 generator: Value,
6758 next: GeneratorState,
6759 ) -> Result<(), EvalFailure> {
6760 let Some(index) = self.runtime_slot(generator).map_err(EvalFailure::Runtime)? else {
6761 return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6762 value: generator,
6763 }));
6764 };
6765 let HeapEntry::Generator { state, .. } = &mut self.heap[index] else {
6766 return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6767 value: generator,
6768 }));
6769 };
6770 if !matches!(state, GeneratorState::Executing) {
6771 return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6772 value: generator,
6773 }));
6774 }
6775 *state = next;
6776 Ok(())
6777 }
6778
6779 pub(crate) fn start_async_call(
6784 &mut self,
6785 target: RuntimeFunction,
6786 captures: &[Value],
6787 this_value: Value,
6788 new_target: Value,
6789 arguments: &[Value],
6790 ) -> Result<Value, EvalFailure> {
6791 let promise = self.create_promise()?;
6792 let record = self.create_async_activation(promise)?;
6793 let stop_depth = self.frames.len();
6794 let return_to = self.frames.last().map(|frame| ReturnTo {
6795 destination: None,
6796 call_pc: frame.pc,
6797 constructed: None,
6798 });
6799 self.push_frame(
6800 target, captures, this_value, new_target, arguments, return_to,
6801 )
6802 .map_err(|error| EvalFailure::Runtime(error.kind))?;
6803 let step = self.drive_async_activation(stop_depth, None);
6804 self.settle_async_step(record, promise, step)?;
6805 Ok(promise)
6806 }
6807
6808 fn resume_async(
6813 &mut self,
6814 record: Value,
6815 value: Value,
6816 rejection: Option<ThrowOrigin>,
6817 ) -> Result<(), RuntimeErrorKind> {
6818 let promise = self.async_activation_promise(record)?;
6819 let activation = self.take_async_activation(record)?;
6820 let register_count = activation.registers.len();
6821 if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
6822 self.release_suspended_activation_registers(register_count);
6823 return Err(RuntimeErrorKind::CallDepthExceeded {
6824 limit: self.limits.max_call_depth,
6825 });
6826 }
6827 let suspend_pc = activation
6828 .resume_token
6829 .checked_sub(1)
6830 .expect("suspended async token is nonzero") as usize;
6831 let instruction = self.module_code(activation.target.module).functions()
6832 [activation.target.function.get() as usize]
6833 .code()[suspend_pc];
6834 let Instruction::Suspend { dst, resume, .. } = instruction else {
6835 unreachable!("async resume token names a suspend instruction");
6836 };
6837 let stop_depth = self.frames.len();
6838 let return_to = self.frames.last().map(|frame| ReturnTo {
6839 destination: None,
6840 call_pc: frame.pc,
6841 constructed: None,
6842 });
6843 let mut frame = Frame {
6844 module: activation.target.module,
6845 function: activation.target.function.get() as usize,
6846 pc: resume.get() as usize,
6847 registers: activation.registers,
6848 return_to,
6849 this_value: activation.this_value,
6850 new_target: activation.new_target,
6851 args: activation.args,
6852 arguments_object: activation.arguments_object,
6853 };
6854 let inject = match rejection {
6855 None => {
6856 frame.registers[dst.get() as usize] = value;
6857 None
6858 }
6859 Some(origin) => Some((value, origin, suspend_pc)),
6860 };
6861 self.frames.push(frame);
6862 let step = self.drive_async_activation(stop_depth, inject);
6863 match self.settle_async_step(record, promise, step) {
6864 Ok(()) => Ok(()),
6865 Err(EvalFailure::Runtime(kind)) => Err(kind),
6866 Err(_) => Err(RuntimeErrorKind::InvalidValue { value: record }),
6867 }
6868 }
6869
6870 fn drive_async_activation(
6876 &mut self,
6877 stop_depth: usize,
6878 inject: Option<(Value, ThrowOrigin, usize)>,
6879 ) -> Result<AsyncStep, EvalFailure> {
6880 self.last_completion = None;
6881 self.pending_async_suspend = None;
6882 self.callback_boundaries.push(stop_depth);
6883 self.async_boundaries.push(stop_depth);
6884 let result = match inject {
6885 None => self.run_loop(stop_depth),
6886 Some((value, origin, faulting_pc)) => match self.throw(value, origin, faulting_pc) {
6887 Ok(()) => self.run_loop(stop_depth),
6888 Err(error) => Err(error),
6889 },
6890 };
6891 self.async_boundaries
6892 .pop()
6893 .expect("async execution owns its suspend boundary");
6894 self.callback_boundaries
6895 .pop()
6896 .expect("async execution owns its unwind boundary");
6897 match result {
6898 Ok(Some(execution)) => Ok(AsyncStep::Return(execution.value)),
6899 Ok(None) => {
6900 if let Some((awaited, activation)) = self.pending_async_suspend.take() {
6901 Ok(AsyncStep::Suspend {
6902 awaited,
6903 activation,
6904 })
6905 } else {
6906 Ok(AsyncStep::Return(
6907 self.last_completion.take().unwrap_or(Value::UNDEFINED),
6908 ))
6909 }
6910 }
6911 Err(error) => {
6912 self.unwind_frames_to(stop_depth);
6913 match error.kind {
6914 RuntimeErrorKind::UncaughtThrow { value, origin } => {
6915 Ok(AsyncStep::Throw { value, origin })
6916 }
6917 kind => Err(EvalFailure::Runtime(kind)),
6918 }
6919 }
6920 }
6921 }
6922
6923 fn settle_async_step(
6925 &mut self,
6926 record: Value,
6927 promise: Value,
6928 step: Result<AsyncStep, EvalFailure>,
6929 ) -> Result<(), EvalFailure> {
6930 match step {
6931 Ok(AsyncStep::Suspend {
6932 awaited,
6933 activation,
6934 }) => {
6935 let register_count = activation.registers.len();
6936 let result = self
6937 .store_async_activation(record, activation)
6938 .and_then(|()| self.await_promise(awaited, record));
6939 if result.is_err() {
6940 let released = self
6941 .take_async_activation(record)
6942 .map_or(register_count, |stored| stored.registers.len());
6943 self.release_suspended_activation_registers(released);
6944 }
6945 result
6946 }
6947 Ok(AsyncStep::Return(value)) => self
6948 .resolve_promise(promise, value)
6949 .map_err(EvalFailure::Runtime),
6950 Ok(AsyncStep::Throw { value, origin }) => self
6951 .reject_promise(promise, value, origin)
6952 .map_err(EvalFailure::Runtime),
6953 Err(failure) => Err(failure),
6954 }
6955 }
6956
6957 fn await_promise(&mut self, awaited: Value, record: Value) -> Result<(), EvalFailure> {
6961 let promise = self.promise_resolve(awaited)?;
6962 let index = self
6963 .runtime_slot(promise)
6964 .map_err(EvalFailure::Runtime)?
6965 .ok_or(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6966 value: promise,
6967 }))?;
6968 let settled = match &self.heap[index] {
6969 HeapEntry::Promise {
6970 state: PromiseState::Pending { .. },
6971 ..
6972 } => None,
6973 HeapEntry::Promise {
6974 state: PromiseState::Fulfilled { value },
6975 ..
6976 } => Some((true, *value, ThrowOrigin::Bytecode)),
6977 HeapEntry::Promise {
6978 state: PromiseState::Rejected { reason, origin },
6979 ..
6980 } => Some((false, *reason, *origin)),
6981 _ => {
6982 return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6983 value: promise,
6984 }));
6985 }
6986 };
6987 if let Some((fulfilled, value, origin)) = settled {
6988 self.ensure_microtask_capacity(1)
6989 .map_err(EvalFailure::Runtime)?;
6990 let reaction = if fulfilled {
6991 PromiseReaction::AsyncFulfill { activation: record }
6992 } else {
6993 PromiseReaction::AsyncReject { activation: record }
6994 };
6995 self.microtasks.push_back(MicrotaskJob::Reaction {
6996 reaction,
6997 value,
6998 origin,
6999 });
7000 return Ok(());
7001 }
7002 self.charge_promise_reactions(2)?;
7003 let HeapEntry::Promise {
7004 state:
7005 PromiseState::Pending {
7006 fulfill_reactions,
7007 reject_reactions,
7008 },
7009 ..
7010 } = &mut self.heap[index]
7011 else {
7012 unreachable!("pending Promise state was checked before reaction registration");
7013 };
7014 fulfill_reactions.push(PromiseReaction::AsyncFulfill { activation: record });
7015 reject_reactions.push(PromiseReaction::AsyncReject { activation: record });
7016 Ok(())
7017 }
7018
7019 fn create_async_activation(&mut self, promise: Value) -> Result<Value, EvalFailure> {
7020 self.allocate(HeapEntry::AsyncActivation {
7021 activation: None,
7022 promise,
7023 })
7024 .map_err(EvalFailure::Runtime)
7025 }
7026
7027 fn store_async_activation(
7028 &mut self,
7029 record: Value,
7030 activation: SuspendedActivation,
7031 ) -> Result<(), EvalFailure> {
7032 let index = self
7033 .runtime_slot(record)
7034 .map_err(EvalFailure::Runtime)?
7035 .ok_or(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
7036 value: record,
7037 }))?;
7038 let HeapEntry::AsyncActivation {
7039 activation: slot, ..
7040 } = &mut self.heap[index]
7041 else {
7042 return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
7043 value: record,
7044 }));
7045 };
7046 *slot = Some(activation);
7047 Ok(())
7048 }
7049
7050 fn take_async_activation(
7053 &mut self,
7054 record: Value,
7055 ) -> Result<SuspendedActivation, RuntimeErrorKind> {
7056 let index = self
7057 .runtime_slot(record)?
7058 .ok_or(RuntimeErrorKind::InvalidValue { value: record })?;
7059 let HeapEntry::AsyncActivation {
7060 activation: slot, ..
7061 } = &mut self.heap[index]
7062 else {
7063 return Err(RuntimeErrorKind::InvalidValue { value: record });
7064 };
7065 slot.take()
7066 .ok_or(RuntimeErrorKind::InvalidValue { value: record })
7067 }
7068
7069 fn async_activation_promise(&self, record: Value) -> Result<Value, RuntimeErrorKind> {
7070 let index = self
7071 .runtime_slot(record)?
7072 .ok_or(RuntimeErrorKind::InvalidValue { value: record })?;
7073 let HeapEntry::AsyncActivation { promise, .. } = &self.heap[index] else {
7074 return Err(RuntimeErrorKind::InvalidValue { value: record });
7075 };
7076 Ok(*promise)
7077 }
7078
7079 pub(crate) fn iterator_result(
7080 &mut self,
7081 value: Value,
7082 done: bool,
7083 ) -> Result<Value, EvalFailure> {
7084 let result = self
7085 .allocate(HeapEntry::Object {
7086 properties: PropertyMap::default(),
7087 prototype: Some(self.intrinsics.object_prototype),
7088 boxed_primitive: None,
7089 extensible: true,
7090 })
7091 .map_err(EvalFailure::Runtime)?;
7092 self.set_data_property(result, "value", value)?;
7093 self.set_data_property(result, "done", Value::boolean(done))?;
7094 Ok(result)
7095 }
7096
7097 fn create_iterator(&mut self, src: Value, kind: IteratorKind) -> Result<Value, EvalFailure> {
7100 if kind == IteratorKind::Keys {
7101 let keys = self.enumerable_keys(src)?;
7102 return self
7103 .allocate(HeapEntry::Iterator {
7104 state: IteratorState::Keys { index: 0, keys },
7105 })
7106 .map_err(EvalFailure::Runtime);
7107 }
7108
7109 let iterator_symbol = self.intrinsics.builtins.symbol_iterator();
7110 let iterator_key = self.to_property_key(iterator_symbol)?;
7111 let method = self.get_property_key(src, &iterator_key)?;
7112 if !self.is_callable(method)? {
7113 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7114 operation: "value is not iterable",
7115 }));
7116 }
7117 let iterator = self.call_value(method, src, &[])?;
7118 if !self.is_object(iterator) {
7119 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7120 operation: "iterator method returned a non-object",
7121 }));
7122 }
7123 let next = self.get_named_property(iterator, "next")?;
7124 self.create_protocol_iterator(iterator, next)
7125 }
7126
7127 pub(crate) fn create_protocol_iterator(
7128 &mut self,
7129 iterator: Value,
7130 next: Value,
7131 ) -> Result<Value, EvalFailure> {
7132 self.allocate(HeapEntry::Iterator {
7133 state: IteratorState::Protocol { iterator, next },
7134 })
7135 .map_err(EvalFailure::Runtime)
7136 }
7137
7138 fn own_property_keys(&self, src: Value) -> Result<Vec<PropertyKey>, EvalFailure> {
7139 match self.runtime_slot(src).map_err(EvalFailure::Runtime)? {
7140 Some(index) => match &self.heap[index] {
7141 HeapEntry::Object { properties, .. }
7142 | HeapEntry::Generator { properties, .. }
7143 | HeapEntry::Script { properties, .. }
7144 | HeapEntry::Function { properties, .. }
7145 | HeapEntry::NativeFunction { properties, .. }
7146 | HeapEntry::RegExp { properties, .. }
7147 | HeapEntry::Date { properties, .. }
7148 | HeapEntry::BuiltinIterator { properties, .. }
7149 | HeapEntry::Collection { properties, .. }
7150 | HeapEntry::Promise { properties, .. }
7151 | HeapEntry::Timeout { properties, .. } => Ok(ordered_property_keys(properties)),
7152 HeapEntry::Array {
7153 elements,
7154 properties,
7155 ..
7156 } => {
7157 let mut indices: Vec<(usize, PropertyKey)> = elements
7158 .iter()
7159 .enumerate()
7160 .filter(|(_, element)| **element != Value::HOLE)
7161 .map(|(offset, _)| {
7162 (
7163 offset,
7164 PropertyKey::Named(EcmaString::from_utf8(&offset.to_string())),
7165 )
7166 })
7167 .collect();
7168 let mut suffix = Vec::new();
7169 for key in ordered_property_keys(properties) {
7170 let Some(offset) = key.as_string().and_then(array_index) else {
7171 suffix.push(key);
7172 continue;
7173 };
7174 let offset = offset as usize;
7175 if elements
7176 .get(offset)
7177 .is_some_and(|element| *element != Value::HOLE)
7178 {
7179 continue;
7180 }
7181 indices.push((offset, key));
7182 }
7183 indices.sort_unstable_by_key(|(offset, _)| *offset);
7184 Ok(indices
7185 .into_iter()
7186 .map(|(_, key)| key)
7187 .chain(suffix)
7188 .collect())
7189 }
7190 HeapEntry::String(text) => Ok((0..text.len_units())
7191 .map(|index| PropertyKey::Named(EcmaString::from_utf8(&index.to_string())))
7192 .collect()),
7193 HeapEntry::ModuleNamespace { module } => {
7194 let mut names: Vec<EcmaString> = self
7195 .program_module(*module)
7196 .exports
7197 .iter()
7198 .map(|export| self.constant_text(*module, export.name).clone())
7199 .collect();
7200 names.sort();
7201 Ok(names.into_iter().map(PropertyKey::Named).collect())
7202 }
7203 HeapEntry::ExternalModuleNamespace { specifier } => Ok(self.registry.external
7204 [specifier]
7205 .exports
7206 .keys()
7207 .cloned()
7208 .map(PropertyKey::Named)
7209 .collect()),
7210 HeapEntry::ProcessEnv { .. }
7211 | HeapEntry::BigInt(_)
7212 | HeapEntry::Symbol { .. }
7213 | HeapEntry::PrivateName { .. }
7214 | HeapEntry::HashState { .. }
7215 | HeapEntry::Iterator { .. }
7216 | HeapEntry::PromiseResolver { .. }
7217 | HeapEntry::PromiseFinally { .. }
7218 | HeapEntry::PromiseAll { .. }
7219 | HeapEntry::AsyncActivation { .. }
7220 | HeapEntry::PromiseAllElement { .. } => Ok(Vec::new()),
7221 },
7222 None => Ok(Vec::new()),
7223 }
7224 }
7225
7226 fn own_property_is_enumerable(
7227 &self,
7228 src: Value,
7229 key: &PropertyKey,
7230 ) -> Result<bool, EvalFailure> {
7231 let Some(index) = self.runtime_slot(src).map_err(EvalFailure::Runtime)? else {
7232 return Ok(false);
7233 };
7234 Ok(match &self.heap[index] {
7235 HeapEntry::Array {
7236 elements,
7237 properties,
7238 ..
7239 } => properties.get(key).map_or_else(
7240 || {
7241 key.as_string().is_some_and(|name| {
7242 array_index(name).is_some_and(|offset| {
7243 elements
7244 .get(offset as usize)
7245 .is_some_and(|element| *element != Value::HOLE)
7246 })
7247 })
7248 },
7249 Property::enumerable,
7250 ),
7251 HeapEntry::String(text) => key.as_string().is_some_and(|name| {
7252 array_index(name).is_some_and(|offset| (offset as usize) < text.len_units())
7253 }),
7254 HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
7255 matches!(key, PropertyKey::Named(_))
7256 }
7257 HeapEntry::Object { properties, .. }
7258 | HeapEntry::Generator { properties, .. }
7259 | HeapEntry::Script { properties, .. }
7260 | HeapEntry::Function { properties, .. }
7261 | HeapEntry::NativeFunction { properties, .. }
7262 | HeapEntry::RegExp { properties, .. }
7263 | HeapEntry::Date { properties, .. }
7264 | HeapEntry::BuiltinIterator { properties, .. }
7265 | HeapEntry::Collection { properties, .. }
7266 | HeapEntry::Promise { properties, .. }
7267 | HeapEntry::Timeout { properties, .. } => {
7268 properties.get(key).is_some_and(Property::enumerable)
7269 }
7270 _ => false,
7271 })
7272 }
7273
7274 fn enumerable_keys(&self, src: Value) -> Result<Vec<EcmaString>, EvalFailure> {
7275 let mut names = Vec::new();
7276 for key in self.own_property_keys(src)? {
7277 if !self.own_property_is_enumerable(src, &key)? {
7278 continue;
7279 }
7280 if let PropertyKey::Named(name) = key {
7281 names.push(name);
7282 }
7283 }
7284 Ok(names)
7285 }
7286
7287 fn iterator_next(&mut self, iterator: Value) -> Result<(bool, Value), EvalFailure> {
7288 let (callee, this_value) = match self.prepare_iterator_next(iterator)? {
7289 IteratorNextPrepared::Ready { done, value } => return Ok((done, value)),
7290 IteratorNextPrepared::Call { callee, this_value } => (callee, this_value),
7291 };
7292
7293 let result = self.call_value(callee, this_value, &[])?;
7294 if !self.is_object(result) {
7295 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7296 operation: "iterator next returned a non-object",
7297 }));
7298 }
7299 let done = self.get_named_property(result, "done")?;
7300 if self.truthy(done) {
7301 return Ok((true, Value::UNDEFINED));
7302 }
7303 let value = self.get_named_property(result, "value")?;
7304 Ok((false, value))
7305 }
7306
7307 pub(crate) fn prepare_iterator_next(
7308 &mut self,
7309 iterator: Value,
7310 ) -> Result<IteratorNextPrepared, EvalFailure> {
7311 let iterator_index = self
7312 .runtime_slot(iterator)
7313 .map_err(EvalFailure::Runtime)?
7314 .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
7315 operation: "iterator next on non-iterator",
7316 }))?;
7317 match &self.heap[iterator_index] {
7318 HeapEntry::Iterator {
7319 state: IteratorState::Keys { index, keys },
7320 } => {
7321 let Some(text) = keys.get(*index).cloned() else {
7322 return Ok(IteratorNextPrepared::Ready {
7323 done: true,
7324 value: Value::UNDEFINED,
7325 });
7326 };
7327 let value = self
7328 .allocate(HeapEntry::String(text))
7329 .map_err(EvalFailure::Runtime)?;
7330 self.advance_iterator(iterator_index);
7331 Ok(IteratorNextPrepared::Ready { done: false, value })
7332 }
7333 HeapEntry::Iterator {
7334 state: IteratorState::Protocol { iterator, next },
7335 } => Ok(IteratorNextPrepared::Call {
7336 callee: *next,
7337 this_value: *iterator,
7338 }),
7339 _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7340 operation: "iterator next on non-iterator",
7341 })),
7342 }
7343 }
7344
7345 pub(crate) fn iterable_values(&mut self, source: Value) -> Result<Vec<Value>, EvalFailure> {
7346 let iterator = self.create_iterator(source, IteratorKind::Sync)?;
7347 let mut values = Vec::new();
7348 loop {
7349 let (done, value) = self.iterator_next(iterator)?;
7350 if done {
7351 return Ok(values);
7352 }
7353 let bytes = values
7354 .len()
7355 .checked_add(1)
7356 .and_then(|length| length.checked_mul(std::mem::size_of::<Value>()))
7357 .ok_or(EvalFailure::Runtime(
7358 RuntimeErrorKind::HeapByteLimitExceeded {
7359 limit: self.limits.max_heap_bytes,
7360 },
7361 ))?;
7362 self.ensure_allocation_capacity(1, bytes)
7363 .map_err(EvalFailure::Runtime)?;
7364 values.push(value);
7365 }
7366 }
7367
7368 fn advance_iterator(&mut self, iterator_index: usize) {
7369 if let HeapEntry::Iterator {
7370 state: IteratorState::Keys { index, .. },
7371 } = &mut self.heap[iterator_index]
7372 {
7373 *index += 1;
7374 }
7375 }
7376
7377 fn eval_unary(&mut self, op: UnaryOp, operand: Value) -> Result<Value, EvalFailure> {
7380 match op {
7381 UnaryOp::Void => Ok(Value::UNDEFINED),
7382 UnaryOp::TypeOf => {
7383 let text = EcmaString::from_utf8(self.type_of(operand));
7384 self.allocate(HeapEntry::String(text))
7385 .map_err(EvalFailure::Runtime)
7386 }
7387 UnaryOp::Plus => self.to_number(operand),
7388 UnaryOp::Negate => {
7389 if let Some(text) = self.bigint_text(operand) {
7390 let negated = if text == "0" {
7391 "0".to_owned()
7392 } else if let Some(positive) = text.strip_prefix('-') {
7393 positive.to_owned()
7394 } else {
7395 format!("-{text}")
7396 };
7397 return self
7398 .allocate(HeapEntry::BigInt(negated))
7399 .map_err(EvalFailure::Runtime);
7400 }
7401 let number =
7402 numeric_f64(self.to_number(operand)?).expect("ToNumber returns numeric");
7403 Ok(number_value(-number))
7404 }
7405 UnaryOp::BitwiseNot => {
7406 if let Some(text) = self.bigint_text(operand) {
7407 let value = text.parse::<i128>().map_err(|_| {
7408 EvalFailure::Throw(ThrowOrigin::RangeError {
7409 operation: "bigint bitwise not",
7410 })
7411 })?;
7412 return self
7413 .allocate(HeapEntry::BigInt((!value).to_string()))
7414 .map_err(EvalFailure::Runtime);
7415 }
7416 Ok(Value::int32(
7417 (!to_int32(numeric_f64(self.to_number(operand)?).unwrap())) as u32,
7418 ))
7419 }
7420 UnaryOp::LogicalNot => Ok(Value::boolean(!self.truthy(operand))),
7421 }
7422 }
7423
7424 fn eval_binary(
7425 &mut self,
7426 op: BinaryOp,
7427 left: Value,
7428 right: Value,
7429 ) -> Result<Value, EvalFailure> {
7430 match op {
7431 BinaryOp::StrictEqual => Ok(Value::boolean(self.strict_equal(left, right))),
7432 BinaryOp::StrictNotEqual => Ok(Value::boolean(!self.strict_equal(left, right))),
7433 BinaryOp::Equal | BinaryOp::NotEqual => {
7434 let equal = self.abstract_equal(left, right)?;
7435 Ok(Value::boolean(if op == BinaryOp::Equal {
7436 equal
7437 } else {
7438 !equal
7439 }))
7440 }
7441 BinaryOp::LessThan
7442 | BinaryOp::LessThanOrEqual
7443 | BinaryOp::GreaterThan
7444 | BinaryOp::GreaterThanOrEqual => {
7445 let ordering = self.relational_compare(left, right)?;
7446 let result = match (op, ordering) {
7447 (_, None) => false,
7448 (BinaryOp::LessThan, Some(order)) => order == Ordering::Less,
7449 (BinaryOp::LessThanOrEqual, Some(order)) => order != Ordering::Greater,
7450 (BinaryOp::GreaterThan, Some(order)) => order == Ordering::Greater,
7451 (BinaryOp::GreaterThanOrEqual, Some(order)) => order != Ordering::Less,
7452 _ => unreachable!(),
7453 };
7454 Ok(Value::boolean(result))
7455 }
7456 BinaryOp::InstanceOf => self.instance_of(left, right).map(Value::boolean),
7457 BinaryOp::In => {
7458 let key = self.to_property_key(left)?;
7459 self.has_property(right, &key).map(Value::boolean)
7460 }
7461 BinaryOp::Add => self.add(left, right),
7462 BinaryOp::Subtract
7463 | BinaryOp::Multiply
7464 | BinaryOp::Divide
7465 | BinaryOp::Remainder
7466 | BinaryOp::Exponent
7467 | BinaryOp::BitAnd
7468 | BinaryOp::BitOr
7469 | BinaryOp::BitXor
7470 | BinaryOp::ShiftLeft
7471 | BinaryOp::ShiftRight
7472 | BinaryOp::UnsignedShiftRight => self.numeric_binary(op, left, right),
7473 }
7474 }
7475
7476 fn add(&mut self, left: Value, right: Value) -> Result<Value, EvalFailure> {
7477 let left = self.to_primitive_default(left)?;
7478 let right = self.to_primitive_default(right)?;
7479 let left_string = self.string_text(left).cloned();
7480 let right_string = self.string_text(right).cloned();
7481 if left_string.is_some() || right_string.is_some() {
7482 let left = match left_string {
7483 Some(text) => text,
7484 None => self.to_string(left)?,
7485 };
7486 let right = match right_string {
7487 Some(text) => text,
7488 None => self.to_string(right)?,
7489 };
7490 let mut builder = EcmaStringBuilder::with_capacity(
7491 left.len_units().saturating_add(right.len_units()),
7492 );
7493 for &unit in left.as_units() {
7494 builder.push_unit(unit);
7495 }
7496 for &unit in right.as_units() {
7497 builder.push_unit(unit);
7498 }
7499 return self
7500 .allocate(HeapEntry::String(builder.finish()))
7501 .map_err(EvalFailure::Runtime);
7502 }
7503 let left_bigint = self.bigint_text(left).map(str::to_owned);
7504 let right_bigint = self.bigint_text(right).map(str::to_owned);
7505 match (left_bigint, right_bigint) {
7506 (Some(left), Some(right)) => {
7507 let sum = bigint_i128(&left)?
7508 .checked_add(bigint_i128(&right)?)
7509 .ok_or(EvalFailure::Throw(ThrowOrigin::RangeError {
7510 operation: "bigint add overflow",
7511 }))?;
7512 return self
7513 .allocate(HeapEntry::BigInt(sum.to_string()))
7514 .map_err(EvalFailure::Runtime);
7515 }
7516 (Some(_), None) | (None, Some(_)) => {
7517 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7518 operation: "add bigint and number",
7519 }));
7520 }
7521 (None, None) => {}
7522 }
7523 let left = numeric_f64(self.to_number(left)?).unwrap();
7524 let right = numeric_f64(self.to_number(right)?).unwrap();
7525 Ok(number_value(left + right))
7526 }
7527
7528 fn numeric_binary(
7529 &mut self,
7530 op: BinaryOp,
7531 left: Value,
7532 right: Value,
7533 ) -> Result<Value, EvalFailure> {
7534 let left_bigint = self.bigint_text(left).map(str::to_owned);
7535 let right_bigint = self.bigint_text(right).map(str::to_owned);
7536 if left_bigint.is_some() || right_bigint.is_some() {
7537 let (Some(left), Some(right)) = (left_bigint, right_bigint) else {
7538 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7539 operation: "mix bigint and number",
7540 }));
7541 };
7542 let result = bigint_binary(op, &left, &right)?;
7543 return self
7544 .allocate(HeapEntry::BigInt(result))
7545 .map_err(EvalFailure::Runtime);
7546 }
7547 let left = numeric_f64(self.to_number(left)?).unwrap();
7548 let right = numeric_f64(self.to_number(right)?).unwrap();
7549 let value = match op {
7550 BinaryOp::Subtract => number_value(left - right),
7551 BinaryOp::Multiply => number_value(left * right),
7552 BinaryOp::Divide => Value::number(left / right),
7553 BinaryOp::Remainder => Value::number(left % right),
7554 BinaryOp::Exponent => Value::number(left.powf(right)),
7555 BinaryOp::BitAnd => Value::int32((to_int32(left) & to_int32(right)) as u32),
7556 BinaryOp::BitOr => Value::int32((to_int32(left) | to_int32(right)) as u32),
7557 BinaryOp::BitXor => Value::int32((to_int32(left) ^ to_int32(right)) as u32),
7558 BinaryOp::ShiftLeft => {
7559 Value::int32(to_int32(left).wrapping_shl(to_uint32(right) & 31) as u32)
7560 }
7561 BinaryOp::ShiftRight => {
7562 Value::int32((to_int32(left) >> (to_uint32(right) & 31)) as u32)
7563 }
7564 BinaryOp::UnsignedShiftRight => {
7565 number_value((to_uint32(left) >> (to_uint32(right) & 31)) as f64)
7566 }
7567 _ => unreachable!("numeric binary operator partition"),
7568 };
7569 Ok(value)
7570 }
7571
7572 fn coercion_is_primitive(&self, value: Value) -> Result<bool, EvalFailure> {
7573 let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
7574 return Ok(true);
7575 };
7576 Ok(matches!(
7577 self.heap[index],
7578 HeapEntry::String(_)
7579 | HeapEntry::BigInt(_)
7580 | HeapEntry::Symbol { .. }
7581 | HeapEntry::PrivateName { .. }
7582 ))
7583 }
7584
7585 fn to_primitive_default(&mut self, value: Value) -> Result<Value, EvalFailure> {
7586 let prefer_string = self
7587 .runtime_slot(value)
7588 .map_err(EvalFailure::Runtime)?
7589 .is_some_and(|index| matches!(self.heap[index], HeapEntry::Date { .. }));
7590 self.to_primitive_observable(value, prefer_string)
7591 }
7592
7593 pub(crate) fn to_primitive_observable(
7594 &mut self,
7595 value: Value,
7596 prefer_string: bool,
7597 ) -> Result<Value, EvalFailure> {
7598 if self.coercion_is_primitive(value)? {
7599 return Ok(value);
7600 }
7601 let methods = if prefer_string {
7602 ["toString", "valueOf"]
7603 } else {
7604 ["valueOf", "toString"]
7605 };
7606 for name in methods {
7607 let method = self.get_named_property(value, name)?;
7608 if !self.is_callable(method)? {
7609 continue;
7610 }
7611 let primitive = self.call_value(method, value, &[])?;
7612 if self.coercion_is_primitive(primitive)? {
7613 return Ok(primitive);
7614 }
7615 }
7616 Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7617 operation: "cannot convert object to primitive",
7618 }))
7619 }
7620
7621 pub(crate) fn to_string_observable(&mut self, value: Value) -> Result<EcmaString, EvalFailure> {
7622 let primitive = self.to_primitive_observable(value, true)?;
7623 self.to_string(primitive)
7624 }
7625
7626 pub(crate) fn to_number_observable(&mut self, value: Value) -> Result<Value, EvalFailure> {
7627 let primitive = self.to_primitive_observable(value, false)?;
7628 self.to_number(primitive)
7629 }
7630
7631 fn to_number(&self, value: Value) -> Result<Value, EvalFailure> {
7632 match value.decode() {
7633 Some(Decoded::Number(_)) | Some(Decoded::Int32(_)) => self.to_primitive(value),
7634 Some(Decoded::Undefined) => Ok(Value::number(f64::NAN)),
7635 Some(Decoded::Null) => Ok(Value::int32(0)),
7636 Some(Decoded::Boolean(value)) => Ok(Value::int32(u32::from(value))),
7637 Some(Decoded::Hole) | Some(Decoded::Uninitialized) => Ok(Value::number(f64::NAN)),
7638 Some(Decoded::HeapRef(_)) => {
7639 match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7640 Some(index) => match &self.heap[index] {
7641 HeapEntry::String(text) => Ok(number_value(parse_number(text))),
7642 HeapEntry::BigInt(_) => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7643 operation: "convert bigint to number",
7644 })),
7645 HeapEntry::Array { elements, .. } if elements.is_empty() => {
7646 Ok(Value::int32(0))
7647 }
7648 HeapEntry::Array { elements, .. } if elements.len() == 1 => {
7649 self.to_number(elements[0])
7650 }
7651 HeapEntry::Symbol { .. } | HeapEntry::PrivateName { .. } => {
7652 Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7653 operation: "convert symbol to number",
7654 }))
7655 }
7656 HeapEntry::Object { .. }
7657 | HeapEntry::Generator { .. }
7658 | HeapEntry::Script { .. }
7659 | HeapEntry::Array { .. }
7660 | HeapEntry::Function { .. }
7661 | HeapEntry::ModuleNamespace { .. }
7662 | HeapEntry::ExternalModuleNamespace { .. }
7663 | HeapEntry::HashState { .. }
7664 | HeapEntry::NativeFunction { .. }
7665 | HeapEntry::RegExp { .. }
7666 | HeapEntry::Date { .. }
7667 | HeapEntry::BuiltinIterator { .. }
7668 | HeapEntry::Collection { .. }
7669 | HeapEntry::Promise { .. }
7670 | HeapEntry::PromiseResolver { .. }
7671 | HeapEntry::PromiseFinally { .. }
7672 | HeapEntry::PromiseAll { .. }
7673 | HeapEntry::AsyncActivation { .. }
7674 | HeapEntry::PromiseAllElement { .. }
7675 | HeapEntry::ProcessEnv { .. }
7676 | HeapEntry::Iterator { .. }
7677 | HeapEntry::Timeout { .. } => Ok(Value::number(f64::NAN)),
7678 },
7679 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7680 operation: "coerce host object to number",
7681 })),
7682 }
7683 }
7684 None => Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
7685 value,
7686 })),
7687 }
7688 }
7689
7690 fn truthy(&self, value: Value) -> bool {
7691 match value.decode() {
7692 Some(Decoded::Number(number)) => number != 0.0 && !number.is_nan(),
7693 Some(Decoded::Int32(value)) => value != 0,
7694 Some(Decoded::Undefined | Decoded::Null | Decoded::Hole | Decoded::Uninitialized)
7695 | None => false,
7696 Some(Decoded::Boolean(value)) => value,
7697 Some(Decoded::HeapRef(_)) => match self.runtime_slot(value) {
7698 Ok(Some(index)) => match &self.heap[index] {
7699 HeapEntry::String(text) => !text.is_empty(),
7700 HeapEntry::BigInt(text) => text != "0",
7701 HeapEntry::Object { .. }
7702 | HeapEntry::Generator { .. }
7703 | HeapEntry::Script { .. }
7704 | HeapEntry::Array { .. }
7705 | HeapEntry::Function { .. }
7706 | HeapEntry::ModuleNamespace { .. }
7707 | HeapEntry::ExternalModuleNamespace { .. }
7708 | HeapEntry::HashState { .. }
7709 | HeapEntry::NativeFunction { .. }
7710 | HeapEntry::Symbol { .. }
7711 | HeapEntry::PrivateName { .. }
7712 | HeapEntry::RegExp { .. }
7713 | HeapEntry::Date { .. }
7714 | HeapEntry::BuiltinIterator { .. }
7715 | HeapEntry::Collection { .. }
7716 | HeapEntry::Promise { .. }
7717 | HeapEntry::PromiseResolver { .. }
7718 | HeapEntry::PromiseFinally { .. }
7719 | HeapEntry::PromiseAll { .. }
7720 | HeapEntry::AsyncActivation { .. }
7721 | HeapEntry::PromiseAllElement { .. }
7722 | HeapEntry::ProcessEnv { .. }
7723 | HeapEntry::Iterator { .. }
7724 | HeapEntry::Timeout { .. } => true,
7725 },
7726 Ok(None) => true,
7727 Err(_) => false,
7728 },
7729 }
7730 }
7731
7732 fn type_of(&self, value: Value) -> &'static str {
7733 match value.decode() {
7734 Some(Decoded::Undefined | Decoded::Hole | Decoded::Uninitialized) | None => "undefined",
7735 Some(Decoded::Number(_) | Decoded::Int32(_)) => "number",
7736 Some(Decoded::Null) => "object",
7737 Some(Decoded::Boolean(_)) => "boolean",
7738 Some(Decoded::HeapRef(_)) => match self.runtime_slot(value) {
7739 Ok(Some(index)) => match &self.heap[index] {
7740 HeapEntry::String(_) => "string",
7741 HeapEntry::BigInt(_) => "bigint",
7742 HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. } => "function",
7743 HeapEntry::Symbol { .. } => "symbol",
7744 HeapEntry::PrivateName { .. } => "object",
7745 HeapEntry::Object { .. }
7746 | HeapEntry::Generator { .. }
7747 | HeapEntry::Script { .. }
7748 | HeapEntry::Array { .. }
7749 | HeapEntry::ModuleNamespace { .. }
7750 | HeapEntry::ExternalModuleNamespace { .. }
7751 | HeapEntry::HashState { .. }
7752 | HeapEntry::RegExp { .. }
7753 | HeapEntry::Date { .. }
7754 | HeapEntry::BuiltinIterator { .. }
7755 | HeapEntry::Collection { .. }
7756 | HeapEntry::Promise { .. }
7757 | HeapEntry::PromiseResolver { .. }
7758 | HeapEntry::PromiseFinally { .. }
7759 | HeapEntry::PromiseAll { .. }
7760 | HeapEntry::AsyncActivation { .. }
7761 | HeapEntry::PromiseAllElement { .. }
7762 | HeapEntry::ProcessEnv { .. }
7763 | HeapEntry::Iterator { .. }
7764 | HeapEntry::Timeout { .. } => "object",
7765 },
7766 _ => "object",
7767 },
7768 }
7769 }
7770
7771 fn strict_equal(&self, left: Value, right: Value) -> bool {
7772 match (left.decode(), right.decode()) {
7773 (Some(Decoded::Number(a)), Some(Decoded::Number(b))) => a == b,
7774 (Some(Decoded::Number(a)), Some(Decoded::Int32(b)))
7775 | (Some(Decoded::Int32(b)), Some(Decoded::Number(a))) => a == f64::from(b as i32),
7776 (Some(Decoded::Int32(a)), Some(Decoded::Int32(b))) => a == b,
7777 (Some(Decoded::HeapRef(_)), Some(Decoded::HeapRef(_))) => {
7778 match (self.runtime_slot(left), self.runtime_slot(right)) {
7779 (Ok(Some(a)), Ok(Some(b))) => match (&self.heap[a], &self.heap[b]) {
7780 (HeapEntry::String(a), HeapEntry::String(b)) => a == b,
7781 (HeapEntry::BigInt(a), HeapEntry::BigInt(b)) => a == b,
7782 _ => left == right,
7783 },
7784 _ => left == right,
7785 }
7786 }
7787 _ => left == right,
7788 }
7789 }
7790
7791 fn abstract_equal(&self, left: Value, right: Value) -> Result<bool, EvalFailure> {
7792 if self.strict_equal(left, right) {
7793 return Ok(true);
7794 }
7795 if matches!(
7796 (left.decode(), right.decode()),
7797 (Some(Decoded::Null), Some(Decoded::Undefined))
7798 | (Some(Decoded::Undefined), Some(Decoded::Null))
7799 ) {
7800 return Ok(true);
7801 }
7802 let left_number = self.to_number(left);
7803 let right_number = self.to_number(right);
7804 match (left_number, right_number) {
7805 (Ok(left), Ok(right)) => Ok(numeric_f64(left).unwrap() == numeric_f64(right).unwrap()),
7806 _ => Ok(false),
7807 }
7808 }
7809
7810 fn relational_compare(
7811 &self,
7812 left: Value,
7813 right: Value,
7814 ) -> Result<Option<Ordering>, EvalFailure> {
7815 if let (Some(left), Some(right)) = (self.string_text(left), self.string_text(right)) {
7816 return Ok(Some(left.cmp(right)));
7817 }
7818 if let (Some(left), Some(right)) = (self.bigint_text(left), self.bigint_text(right)) {
7819 return Ok(Some(bigint_i128(left)?.cmp(&bigint_i128(right)?)));
7820 }
7821 let left = numeric_f64(self.to_number(left)?).unwrap();
7822 let right = numeric_f64(self.to_number(right)?).unwrap();
7823 Ok(left.partial_cmp(&right))
7824 }
7825
7826 fn instance_of(&mut self, value: Value, constructor: Value) -> Result<bool, EvalFailure> {
7829 let constructor = self
7830 .bound_target(constructor)
7831 .map_err(EvalFailure::Runtime)?;
7832 match self
7833 .runtime_slot(constructor)
7834 .map_err(EvalFailure::Runtime)?
7835 {
7836 Some(index) => {
7837 if !matches!(
7838 self.heap[index],
7839 HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. }
7840 ) {
7841 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7842 operation: "instanceof",
7843 }));
7844 }
7845 let target = match self.own_get_ascii(index, "prototype") {
7846 Some(Found::Value(value)) if self.is_object(value) => value,
7847 _ => {
7848 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7849 operation: "instanceof prototype is not an object",
7850 }));
7851 }
7852 };
7853 let target_slot = self.runtime_slot(target).map_err(EvalFailure::Runtime)?;
7854 let mut node = match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7855 Some(node) => node,
7856 None => return Ok(false),
7857 };
7858 let mut guard = 0;
7859 loop {
7860 if Some(node) == target_slot {
7861 return Ok(true);
7862 }
7863 match self.prototype_index(node)? {
7864 Some(next) => {
7865 node = next;
7866 guard += 1;
7867 if guard > self.heap.len() + 1 {
7868 return Ok(false);
7869 }
7870 }
7871 None => return Ok(false),
7872 }
7873 }
7874 }
7875 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7876 operation: "instanceof",
7877 })),
7878 }
7879 }
7880
7881 fn value_to_string(&self, value: Value, depth: usize) -> Result<EcmaString, EvalFailure> {
7882 if depth >= 32 {
7883 return Ok(EcmaString::default());
7884 }
7885 let ascii = |text: String| EcmaString::from_utf8(&text);
7886 match value.decode() {
7887 Some(Decoded::Number(number)) => Ok(ascii(Self::ordinary_number_to_string(number))),
7888 Some(Decoded::Int32(raw)) => Ok(ascii((raw as i32).to_string())),
7889 Some(Decoded::Undefined | Decoded::Uninitialized) => {
7890 Ok(EcmaString::from_utf8("undefined"))
7891 }
7892 Some(Decoded::Null) => Ok(EcmaString::from_utf8("null")),
7893 Some(Decoded::Boolean(value)) => {
7894 Ok(EcmaString::from_utf8(if value { "true" } else { "false" }))
7895 }
7896 Some(Decoded::Hole) => Ok(EcmaString::default()),
7897 Some(Decoded::HeapRef(_)) => {
7898 match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7899 Some(index) => match &self.heap[index] {
7900 HeapEntry::String(text) => Ok(text.clone()),
7901 HeapEntry::BigInt(text) => Ok(EcmaString::from_utf8(text)),
7902 HeapEntry::Object { .. }
7903 | HeapEntry::Generator { .. }
7904 | HeapEntry::Script { .. }
7905 | HeapEntry::Date { .. }
7906 | HeapEntry::BuiltinIterator { .. }
7907 | HeapEntry::Collection { .. }
7908 | HeapEntry::Promise { .. }
7909 | HeapEntry::PromiseResolver { .. }
7910 | HeapEntry::PromiseFinally { .. }
7911 | HeapEntry::PromiseAll { .. }
7912 | HeapEntry::AsyncActivation { .. }
7913 | HeapEntry::PromiseAllElement { .. }
7914 | HeapEntry::ModuleNamespace { .. }
7915 | HeapEntry::ExternalModuleNamespace { .. }
7916 | HeapEntry::ProcessEnv { .. }
7917 | HeapEntry::Iterator { .. }
7918 | HeapEntry::Timeout { .. }
7919 | HeapEntry::HashState { .. } => {
7920 Ok(EcmaString::from_utf8("[object Object]"))
7921 }
7922 HeapEntry::RegExp { pattern, flags, .. } => {
7923 let mut builder = EcmaStringBuilder::with_capacity(
7924 pattern
7925 .len_units()
7926 .saturating_add(flags.len_units())
7927 .saturating_add(2),
7928 );
7929 builder.push_unit(u16::from(b'/'));
7930 for &unit in pattern.as_units() {
7931 builder.push_unit(unit);
7932 }
7933 builder.push_unit(u16::from(b'/'));
7934 for &unit in flags.as_units() {
7935 builder.push_unit(unit);
7936 }
7937 Ok(builder.finish())
7938 }
7939 HeapEntry::Symbol { .. } => {
7940 Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7941 operation: "convert symbol to string",
7942 }))
7943 }
7944 HeapEntry::PrivateName { .. } => {
7945 Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7946 operation: "convert private name to string",
7947 }))
7948 }
7949 HeapEntry::Function {
7950 module, function, ..
7951 } => {
7952 let flags = self.module_code(*module).functions()
7953 [function.get() as usize]
7954 .flags();
7955 Ok(EcmaString::from_utf8(
7956 match (flags.is_async, flags.is_generator) {
7957 (true, true) => "async function* () { [bytecode] }",
7958 (true, false) => "async function () { [bytecode] }",
7959 (false, true) => "function* () { [bytecode] }",
7960 (false, false) => "function () { [bytecode] }",
7961 },
7962 ))
7963 }
7964 HeapEntry::NativeFunction { .. } => {
7965 Ok(EcmaString::from_utf8("function () { [native code] }"))
7966 }
7967 HeapEntry::Array { elements, .. } => {
7968 let mut text = EcmaStringBuilder::new();
7969 for (index, element) in elements.iter().copied().enumerate() {
7970 if index != 0 {
7971 text.push_unit(u16::from(b','));
7972 }
7973 if element != Value::HOLE
7974 && element != Value::NULL
7975 && element != Value::UNDEFINED
7976 {
7977 for &unit in
7978 self.value_to_string(element, depth + 1)?.as_units()
7979 {
7980 text.push_unit(unit);
7981 }
7982 }
7983 }
7984 Ok(text.finish())
7985 }
7986 },
7987 None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7988 operation: "coerce host object to string",
7989 })),
7990 }
7991 }
7992 None => Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
7993 value,
7994 })),
7995 }
7996 }
7997
7998 fn string_text(&self, value: Value) -> Option<&EcmaString> {
7999 let index = self.runtime_slot(value).ok()??;
8000 match &self.heap[index] {
8001 HeapEntry::String(text) => Some(text),
8002 _ => None,
8003 }
8004 }
8005
8006 fn bigint_text(&self, value: Value) -> Option<&str> {
8007 let index = self.runtime_slot(value).ok()??;
8008 match &self.heap[index] {
8009 HeapEntry::BigInt(text) => Some(text),
8010 _ => None,
8011 }
8012 }
8013
8014 fn is_object(&self, value: Value) -> bool {
8015 match self.runtime_slot(value) {
8016 Ok(Some(index)) => !matches!(
8017 self.heap[index],
8018 HeapEntry::String(_)
8019 | HeapEntry::BigInt(_)
8020 | HeapEntry::PromiseResolver { .. }
8021 | HeapEntry::PromiseFinally { .. }
8022 | HeapEntry::PromiseAll { .. }
8023 | HeapEntry::AsyncActivation { .. }
8024 | HeapEntry::PromiseAllElement { .. }
8025 ),
8026 Ok(None) => matches!(value.decode(), Some(Decoded::HeapRef(_))),
8027 Err(_) => false,
8028 }
8029 }
8030}
8031
8032fn ordered_property_keys(properties: &PropertyMap) -> Vec<PropertyKey> {
8033 let mut indices = Vec::new();
8034 let mut strings = Vec::new();
8035 let mut symbols = Vec::new();
8036 for (key, _) in properties.iter() {
8037 match key {
8038 PropertyKey::Named(name) => match array_index(name) {
8039 Some(index) => indices.push((index, key.clone())),
8040 None => strings.push(key.clone()),
8041 },
8042 PropertyKey::Symbol(_) => symbols.push(key.clone()),
8043 PropertyKey::Private(_) => {}
8044 }
8045 }
8046 indices.sort_unstable_by_key(|(index, _)| *index);
8047 indices
8048 .into_iter()
8049 .map(|(_, key)| key)
8050 .chain(strings)
8051 .chain(symbols)
8052 .collect()
8053}
8054
8055fn property_lookup(properties: &PropertyMap, key: &PropertyKey) -> Option<Found> {
8056 match properties.get(key) {
8057 Some(Property::Data { value, .. }) => Some(Found::Value(*value)),
8058 Some(Property::Accessor { getter, .. }) => Some(match getter {
8059 Some(getter) => Found::Getter(*getter),
8060 None => Found::NoGetter,
8061 }),
8062 None => None,
8063 }
8064}
8065
8066fn property_lookup_ascii(properties: &PropertyMap, name: &str) -> Option<Found> {
8067 match properties.get_ascii(name) {
8068 Some(Property::Data { value, .. }) => Some(Found::Value(*value)),
8069 Some(Property::Accessor { getter, .. }) => Some(match getter {
8070 Some(getter) => Found::Getter(*getter),
8071 None => Found::NoGetter,
8072 }),
8073 None => None,
8074 }
8075}
8076
8077fn innermost_handler(function: &Function, pc: usize) -> Option<bamts_bytecode::ExceptionHandler> {
8078 function
8079 .handlers()
8080 .iter()
8081 .copied()
8082 .filter(|handler| handler.start.get() as usize <= pc && pc < handler.end.get() as usize)
8083 .max_by(|left, right| {
8084 left.start
8085 .get()
8086 .cmp(&right.start.get())
8087 .then_with(|| right.end.get().cmp(&left.end.get()))
8088 })
8089}
8090
8091fn numeric_f64(value: Value) -> Option<f64> {
8092 match value.decode()? {
8093 Decoded::Number(number) => Some(number),
8094 Decoded::Int32(raw) => Some(f64::from(raw as i32)),
8095 _ => None,
8096 }
8097}
8098
8099fn number_value(number: f64) -> Value {
8100 if number.is_finite()
8101 && number.fract() == 0.0
8102 && number >= f64::from(i32::MIN)
8103 && number <= f64::from(i32::MAX)
8104 {
8105 Value::int32(number as i32 as u32)
8106 } else {
8107 Value::number(number)
8108 }
8109}
8110
8111fn parse_number(text: &EcmaString) -> f64 {
8112 let Ok(text) = text.to_utf8_strict() else {
8113 return f64::NAN;
8114 };
8115 parse_number_utf8(&text)
8116}
8117
8118fn parse_number_utf8(text: &str) -> f64 {
8119 let trimmed = text.trim();
8120 if trimmed.is_empty() {
8121 0.0
8122 } else {
8123 trimmed.parse::<f64>().unwrap_or(f64::NAN)
8124 }
8125}
8126
8127fn format_number(number: f64) -> String {
8128 if number.is_nan() {
8129 return "NaN".to_owned();
8130 }
8131 if number == f64::INFINITY {
8132 return "Infinity".to_owned();
8133 }
8134 if number == f64::NEG_INFINITY {
8135 return "-Infinity".to_owned();
8136 }
8137 if number == 0.0 {
8138 return "0".to_owned();
8139 }
8140
8141 let negative = number.is_sign_negative();
8142 let raw = number.abs().to_string();
8143 let (mantissa, explicit_exponent) = match raw.split_once(['e', 'E']) {
8144 Some((mantissa, exponent)) => (
8145 mantissa,
8146 exponent
8147 .parse::<i32>()
8148 .expect("Rust formats finite f64 exponents as i32"),
8149 ),
8150 None => (raw.as_str(), 0),
8151 };
8152 let decimal = mantissa.find('.').unwrap_or(mantissa.len());
8153 let untrimmed: String = mantissa.chars().filter(|ch| *ch != '.').collect();
8154 let first = untrimmed
8155 .find(|ch| ch != '0')
8156 .expect("a nonzero number has a nonzero decimal digit");
8157 let digits = untrimmed[first..].trim_end_matches('0');
8158 let exponent = explicit_exponent + decimal as i32 - first as i32 - 1;
8159
8160 let mut result = String::new();
8161 if negative {
8162 result.push('-');
8163 }
8164 if !(-6..21).contains(&exponent) {
8165 result.push(digits.as_bytes()[0] as char);
8166 if digits.len() > 1 {
8167 result.push('.');
8168 result.push_str(&digits[1..]);
8169 }
8170 result.push('e');
8171 if exponent >= 0 {
8172 result.push('+');
8173 }
8174 result.push_str(&exponent.to_string());
8175 } else if exponent >= 0 {
8176 let integer_digits = exponent as usize + 1;
8177 if digits.len() <= integer_digits {
8178 result.push_str(digits);
8179 result.extend(std::iter::repeat_n('0', integer_digits - digits.len()));
8180 } else {
8181 result.push_str(&digits[..integer_digits]);
8182 result.push('.');
8183 result.push_str(&digits[integer_digits..]);
8184 }
8185 } else {
8186 result.push_str("0.");
8187 result.extend(std::iter::repeat_n('0', (-exponent - 1) as usize));
8188 result.push_str(digits);
8189 }
8190 result
8191}
8192
8193fn to_uint32(number: f64) -> u32 {
8194 if !number.is_finite() || number == 0.0 {
8195 0
8196 } else {
8197 number.trunc().rem_euclid(4_294_967_296.0) as u32
8198 }
8199}
8200
8201fn to_int32(number: f64) -> i32 {
8202 to_uint32(number) as i32
8203}
8204
8205fn array_index_ascii(key: &str) -> Option<u32> {
8206 if !key.is_ascii() || key.is_empty() || (key.len() > 1 && key.as_bytes()[0] == b'0') {
8207 return None;
8208 }
8209 let mut index = 0_u32;
8210 for byte in key.bytes() {
8211 if !byte.is_ascii_digit() {
8212 return None;
8213 }
8214 index = index.checked_mul(10)?.checked_add(u32::from(byte - b'0'))?;
8215 }
8216 (index != u32::MAX).then_some(index)
8217}
8218
8219fn array_index(key: &EcmaString) -> Option<u32> {
8220 let units = key.as_units();
8221 if units.is_empty() || (units.len() > 1 && units[0] == u16::from(b'0')) {
8222 return None;
8223 }
8224 let mut index = 0_u32;
8225 for &unit in units {
8226 if !(u16::from(b'0')..=u16::from(b'9')).contains(&unit) {
8227 return None;
8228 }
8229 index = index
8230 .checked_mul(10)?
8231 .checked_add(u32::from(unit - u16::from(b'0')))?;
8232 }
8233 (index != u32::MAX).then_some(index)
8234}
8235
8236fn exact_array_length(value: Value) -> Option<usize> {
8237 let number = numeric_f64(value)?;
8238 if number.is_finite() && number >= 0.0 && number.fract() == 0.0 && number <= u32::MAX as f64 {
8239 Some(number as usize)
8240 } else {
8241 None
8242 }
8243}
8244
8245pub(crate) fn apply_array_length(
8246 elements: &mut Vec<Value>,
8247 properties: &mut PropertyMap,
8248 length: usize,
8249 operation: &'static str,
8250) -> Result<(), EvalFailure> {
8251 if length >= elements.len() {
8252 elements.resize(length, Value::HOLE);
8253 return Ok(());
8254 }
8255 let blocked = properties
8256 .iter()
8257 .filter_map(|(key, property)| {
8258 (!property.configurable())
8259 .then(|| key.as_string().and_then(array_index))
8260 .flatten()
8261 })
8262 .map(|offset| offset as usize)
8263 .filter(|offset| *offset >= length)
8264 .max();
8265 let effective_length = blocked.map_or(length, |offset| offset + 1);
8266 properties.0.retain(|(key, _)| {
8267 key.as_string()
8268 .and_then(array_index)
8269 .is_none_or(|offset| (offset as usize) < effective_length)
8270 });
8271 elements.resize(effective_length, Value::HOLE);
8272 if blocked.is_some() {
8273 return Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }));
8274 }
8275 Ok(())
8276}
8277
8278pub(crate) fn array_set_length(
8279 elements: &mut Vec<Value>,
8280 properties: &mut PropertyMap,
8281 length_writable: bool,
8282 value: Value,
8283 operation: &'static str,
8284) -> Result<(), EvalFailure> {
8285 let length = exact_array_length(value)
8286 .ok_or(EvalFailure::Throw(ThrowOrigin::RangeError { operation }))?;
8287 if !length_writable {
8288 return Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }));
8289 }
8290 apply_array_length(elements, properties, length, operation)
8291}
8292
8293fn bigint_i128(text: &str) -> Result<i128, EvalFailure> {
8294 text.parse::<i128>().map_err(|_| {
8295 EvalFailure::Throw(ThrowOrigin::RangeError {
8296 operation: "bigint magnitude exceeds runtime width",
8297 })
8298 })
8299}
8300
8301fn bigint_binary(op: BinaryOp, left: &str, right: &str) -> Result<String, EvalFailure> {
8302 let left = bigint_i128(left)?;
8303 let right = bigint_i128(right)?;
8304 let overflow =
8305 |operation: &'static str| EvalFailure::Throw(ThrowOrigin::RangeError { operation });
8306 let result = match op {
8307 BinaryOp::Subtract => left
8308 .checked_sub(right)
8309 .ok_or_else(|| overflow("bigint subtract overflow"))?,
8310 BinaryOp::Multiply => left
8311 .checked_mul(right)
8312 .ok_or_else(|| overflow("bigint multiply overflow"))?,
8313 BinaryOp::Divide => {
8314 if right == 0 {
8315 return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8316 operation: "bigint division by zero",
8317 }));
8318 }
8319 left.checked_div(right)
8320 .ok_or_else(|| overflow("bigint divide overflow"))?
8321 }
8322 BinaryOp::Remainder => {
8323 if right == 0 {
8324 return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8325 operation: "bigint remainder by zero",
8326 }));
8327 }
8328 left.checked_rem(right)
8329 .ok_or_else(|| overflow("bigint remainder overflow"))?
8330 }
8331 BinaryOp::Exponent => {
8332 if right < 0 {
8333 return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8334 operation: "bigint negative exponent",
8335 }));
8336 }
8337 let exponent =
8338 u32::try_from(right).map_err(|_| overflow("bigint exponent overflow"))?;
8339 left.checked_pow(exponent)
8340 .ok_or_else(|| overflow("bigint exponent overflow"))?
8341 }
8342 BinaryOp::BitAnd => left & right,
8343 BinaryOp::BitOr => left | right,
8344 BinaryOp::BitXor => left ^ right,
8345 BinaryOp::ShiftLeft | BinaryOp::ShiftRight => {
8346 let left_shift = (op == BinaryOp::ShiftLeft) == (right >= 0);
8347 let amount =
8348 u32::try_from(right.unsigned_abs()).map_err(|_| overflow("bigint shift width"))?;
8349 let shifted = if left_shift {
8350 left.checked_shl(amount)
8351 } else {
8352 left.checked_shr(amount)
8353 };
8354 shifted.ok_or_else(|| overflow("bigint shift overflow"))?
8355 }
8356 BinaryOp::UnsignedShiftRight => {
8357 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
8358 operation: "unsigned shift on bigint",
8359 }));
8360 }
8361 _ => unreachable!("bigint arithmetic partition"),
8362 };
8363 Ok(result.to_string())
8364}
8365
8366pub(crate) fn unary_from_selector(op: u32) -> Option<UnaryOp> {
8367 match op {
8368 0 => Some(UnaryOp::Void),
8369 1 => Some(UnaryOp::TypeOf),
8370 2 => Some(UnaryOp::Plus),
8371 3 => Some(UnaryOp::Negate),
8372 4 => Some(UnaryOp::BitwiseNot),
8373 5 => Some(UnaryOp::LogicalNot),
8374 _ => None,
8375 }
8376}
8377
8378pub(crate) fn binary_from_selector(op: u32) -> Option<BinaryOp> {
8379 match op {
8380 0 => Some(BinaryOp::Add),
8381 1 => Some(BinaryOp::Subtract),
8382 2 => Some(BinaryOp::Multiply),
8383 3 => Some(BinaryOp::Divide),
8384 4 => Some(BinaryOp::Remainder),
8385 5 => Some(BinaryOp::Exponent),
8386 6 => Some(BinaryOp::BitAnd),
8387 7 => Some(BinaryOp::BitOr),
8388 8 => Some(BinaryOp::BitXor),
8389 9 => Some(BinaryOp::ShiftLeft),
8390 10 => Some(BinaryOp::ShiftRight),
8391 11 => Some(BinaryOp::UnsignedShiftRight),
8392 12 => Some(BinaryOp::Equal),
8393 13 => Some(BinaryOp::NotEqual),
8394 14 => Some(BinaryOp::StrictEqual),
8395 15 => Some(BinaryOp::StrictNotEqual),
8396 16 => Some(BinaryOp::LessThan),
8397 17 => Some(BinaryOp::LessThanOrEqual),
8398 18 => Some(BinaryOp::GreaterThan),
8399 19 => Some(BinaryOp::GreaterThanOrEqual),
8400 20 => Some(BinaryOp::InstanceOf),
8401 21 => Some(BinaryOp::In),
8402 _ => None,
8403 }
8404}
8405
8406pub(crate) fn iterator_kind_from_selector(kind: u32) -> Option<IteratorKind> {
8407 match kind {
8408 0 => Some(IteratorKind::Sync),
8409 1 => Some(IteratorKind::Async),
8410 2 => Some(IteratorKind::Keys),
8411 _ => None,
8412 }
8413}
8414
8415pub(crate) fn accessor_from_selector(kind: u32) -> Option<AccessorKind> {
8416 match kind {
8417 0 => Some(AccessorKind::Getter),
8418 1 => Some(AccessorKind::Setter),
8419 _ => None,
8420 }
8421}
8422
8423#[cfg(test)]
8424mod tests {
8425 use std::sync::Arc;
8426
8427 use super::*;
8428 use crate::intrinsics::BuiltinOutcome;
8429 use bamts_bytecode::{
8430 Binding, Edge, EdgeKind, ExceptionHandler, Export, ExportSource, FunctionFlags, NumberBits,
8431 ProgramModule, Register,
8432 };
8433
8434 fn reg(raw: u32) -> Register {
8435 Register::new(raw)
8436 }
8437 fn pc(raw: u32) -> Pc {
8438 Pc::new(raw)
8439 }
8440 fn cid(raw: u32) -> ConstantId {
8441 ConstantId::new(raw)
8442 }
8443
8444 fn function(
8446 parameters: u32,
8447 registers: u32,
8448 code: Vec<Instruction>,
8449 handlers: Vec<ExceptionHandler>,
8450 ) -> Function {
8451 Function::new(
8452 None,
8453 0,
8454 parameters,
8455 registers,
8456 FunctionFlags::default(),
8457 code,
8458 handlers,
8459 )
8460 }
8461
8462 fn generator_function(
8463 parameters: u32,
8464 registers: u32,
8465 code: Vec<Instruction>,
8466 handlers: Vec<ExceptionHandler>,
8467 ) -> Function {
8468 Function::new(
8469 None,
8470 0,
8471 parameters,
8472 registers,
8473 FunctionFlags {
8474 is_async: false,
8475 is_generator: true,
8476 },
8477 code,
8478 handlers,
8479 )
8480 }
8481
8482 fn async_function(
8483 parameters: u32,
8484 registers: u32,
8485 code: Vec<Instruction>,
8486 handlers: Vec<ExceptionHandler>,
8487 ) -> Function {
8488 Function::new(
8489 None,
8490 0,
8491 parameters,
8492 registers,
8493 FunctionFlags {
8494 is_async: true,
8495 is_generator: false,
8496 },
8497 code,
8498 handlers,
8499 )
8500 }
8501
8502 fn closure_function(
8504 captures: u32,
8505 parameters: u32,
8506 registers: u32,
8507 code: Vec<Instruction>,
8508 ) -> Function {
8509 Function::new(
8510 None,
8511 captures,
8512 parameters,
8513 registers,
8514 FunctionFlags::default(),
8515 code,
8516 Vec::new(),
8517 )
8518 }
8519
8520 fn verified(mut constants: Vec<Constant>, functions: Vec<Function>) -> Program<Verified> {
8521 let name = ConstantId::new(constants.len() as u32);
8522 constants.push(Constant::String(EcmaString::from_utf8("<test>")));
8523 let code = Module::new(constants, functions, FunctionId::new(0))
8524 .verify()
8525 .expect("valid test bytecode");
8526 Program::link(
8527 vec![ProgramModule {
8528 name,
8529 code,
8530 edges: Vec::new(),
8531 bindings: Vec::new(),
8532 exports: Vec::new(),
8533 }],
8534 ModuleId::new(0),
8535 )
8536 .expect("valid one-module test program")
8537 }
8538 fn program_module(
8539 name: &str,
8540 mut constants: Vec<Constant>,
8541 functions: Vec<Function>,
8542 edges: Vec<Edge>,
8543 bindings: Vec<Binding>,
8544 exports: Vec<Export>,
8545 ) -> ProgramModule<Verified> {
8546 constants.insert(0, Constant::String(EcmaString::from_utf8(name)));
8547 let code = Module::new(constants, functions, FunctionId::new(0))
8548 .verify()
8549 .expect("valid test bytecode");
8550 ProgramModule {
8551 name: ConstantId::new(0),
8552 code,
8553 edges,
8554 bindings,
8555 exports,
8556 }
8557 }
8558
8559 fn linked(modules: Vec<ProgramModule<Verified>>, entry: u32) -> Program<Verified> {
8560 Program::link(modules, ModuleId::new(entry)).expect("valid linked test program")
8561 }
8562
8563 fn namespace_descriptor_entry() -> Function {
8564 function(
8565 0,
8566 7,
8567 vec![
8568 Instruction::LoadGlobal {
8569 dst: reg(0),
8570 name: cid(1),
8571 },
8572 Instruction::LoadGlobal {
8573 dst: reg(1),
8574 name: cid(3),
8575 },
8576 Instruction::LoadConst {
8577 dst: reg(2),
8578 constant: cid(4),
8579 },
8580 Instruction::GetProperty {
8581 dst: reg(3),
8582 object: reg(1),
8583 key: reg(2),
8584 },
8585 Instruction::CreateArray { dst: reg(4) },
8586 Instruction::ArrayPush {
8587 array: reg(4),
8588 value: reg(0),
8589 },
8590 Instruction::LoadConst {
8591 dst: reg(5),
8592 constant: cid(5),
8593 },
8594 Instruction::ArrayPush {
8595 array: reg(4),
8596 value: reg(5),
8597 },
8598 Instruction::Call {
8599 dst: reg(6),
8600 callee: reg(3),
8601 this_value: reg(4),
8602 arguments: reg(4),
8603 },
8604 Instruction::Return { value: reg(6) },
8605 ],
8606 Vec::new(),
8607 )
8608 }
8609
8610 #[derive(Default)]
8611 struct TestHost;
8612 impl Host for TestHost {}
8613
8614 #[test]
8615 fn async_await_setup_failure_releases_suspended_registers() {
8616 let program = verified(
8617 vec![Constant::Undefined],
8618 vec![
8619 function(0, 1, vec![Instruction::Halt], Vec::new()),
8620 async_function(
8621 0,
8622 2,
8623 vec![
8624 Instruction::LoadConst {
8625 dst: reg(0),
8626 constant: cid(0),
8627 },
8628 Instruction::Suspend {
8629 dst: reg(1),
8630 src: reg(0),
8631 resume: pc(2),
8632 },
8633 Instruction::Return { value: reg(1) },
8634 ],
8635 Vec::new(),
8636 ),
8637 ],
8638 );
8639 let mut host = TestHost;
8640 let limits = Limits {
8641 max_microtasks: 0,
8642 ..Limits::default()
8643 };
8644 let mut machine = Machine::new(&program, &mut host, limits);
8645 machine.frames.clear();
8646 machine.live_registers = 0;
8647 let callable = generator_callable(&mut machine, 1);
8648
8649 assert!(matches!(
8650 machine.call_value(callable, Value::UNDEFINED, &[]),
8651 Err(EvalFailure::Runtime(
8652 RuntimeErrorKind::MicrotaskQueueLimitExceeded { limit: 0 }
8653 ))
8654 ));
8655 assert_eq!(machine.live_registers, 0);
8656 }
8657
8658 fn run_ok(program: &Program<Verified>) -> Execution {
8659 let mut host = TestHost;
8660 Machine::new(program, &mut host, Limits::default())
8661 .run()
8662 .unwrap()
8663 }
8664
8665 fn generator_callable<H: Host>(machine: &mut Machine<'_, H>, function: u32) -> Value {
8666 machine
8667 .allocate(HeapEntry::Function {
8668 module: ModuleId::new(0),
8669 function: FunctionId::new(function),
8670 captures: Vec::new(),
8671 properties: PropertyMap::default(),
8672 prototype: Some(machine.intrinsics.function_prototype),
8673 extensible: true,
8674 })
8675 .unwrap()
8676 }
8677
8678 fn generator_next<H: Host>(
8679 machine: &mut Machine<'_, H>,
8680 generator: Value,
8681 resume_value: Value,
8682 ) -> Result<(Value, bool), EvalFailure> {
8683 let next = machine.get_named_property(generator, "next")?;
8684 let result = machine.call_value(next, generator, &[resume_value])?;
8685 let done = machine.get_named_property(result, "done")?;
8686 let value = machine.get_named_property(result, "value")?;
8687 Ok((value, machine.truthy(done)))
8688 }
8689
8690 #[test]
8691 fn sync_generator_is_lazy_resumes_registers_and_stays_completed() {
8692 let program = verified(
8693 vec![Constant::Int32(10)],
8694 vec![
8695 function(0, 1, vec![Instruction::Halt], Vec::new()),
8696 generator_function(
8697 0,
8698 3,
8699 vec![
8700 Instruction::LoadConst {
8701 dst: reg(0),
8702 constant: cid(0),
8703 },
8704 Instruction::Suspend {
8705 dst: reg(1),
8706 src: reg(0),
8707 resume: pc(2),
8708 },
8709 Instruction::Binary {
8710 dst: reg(2),
8711 op: BinaryOp::Add,
8712 left: reg(0),
8713 right: reg(1),
8714 },
8715 Instruction::Return { value: reg(2) },
8716 ],
8717 Vec::new(),
8718 ),
8719 ],
8720 );
8721 let mut host = TestHost;
8722 let mut machine = Machine::new(&program, &mut host, Limits::default());
8723 machine.frames.clear();
8724 machine.live_registers = 0;
8725 let callable = generator_callable(&mut machine, 1);
8726 let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8727 assert_eq!(machine.live_registers, 0, "calling must not start the body");
8728 machine
8729 .set_data_property(generator, "visible", Value::int32(1))
8730 .unwrap();
8731 assert_eq!(
8732 machine.get_named_property(generator, "visible").unwrap(),
8733 Value::int32(1),
8734 );
8735 assert_eq!(
8736 machine.own_property_keys(generator).unwrap(),
8737 vec![PropertyKey::Named(EcmaString::from_utf8("visible"))],
8738 );
8739 assert!(
8740 machine
8741 .inherits_from_prototype(
8742 generator,
8743 machine.intrinsics.builtins.generator_prototype(),
8744 )
8745 .unwrap()
8746 );
8747
8748 assert_eq!(
8749 generator_next(&mut machine, generator, Value::int32(99)).unwrap(),
8750 (Value::int32(10), false),
8751 );
8752 assert_eq!(machine.live_registers, 3);
8753 assert_eq!(
8754 generator_next(&mut machine, generator, Value::int32(5)).unwrap(),
8755 (Value::int32(15), true),
8756 );
8757 assert_eq!(machine.live_registers, 0);
8758 assert_eq!(
8759 generator_next(&mut machine, generator, Value::int32(8)).unwrap(),
8760 (Value::UNDEFINED, true),
8761 );
8762 }
8763
8764 #[test]
8765 fn sync_generator_reentrant_next_is_a_type_error() {
8766 let program = verified(
8767 Vec::new(),
8768 vec![
8769 function(0, 1, vec![Instruction::Halt], Vec::new()),
8770 generator_function(0, 1, vec![Instruction::Halt], Vec::new()),
8771 ],
8772 );
8773 let mut host = TestHost;
8774 let mut machine = Machine::new(&program, &mut host, Limits::default());
8775 machine.frames.clear();
8776 machine.live_registers = 0;
8777 let callable = generator_callable(&mut machine, 1);
8778 let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8779 let _ = machine.take_generator_state(generator).unwrap();
8780
8781 assert!(matches!(
8782 generator_next(&mut machine, generator, Value::UNDEFINED),
8783 Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
8784 ));
8785 }
8786
8787 #[test]
8788 fn sync_generator_uncaught_throw_preserves_origin_and_completes() {
8789 let program = verified(
8790 vec![Constant::Int32(7)],
8791 vec![
8792 function(0, 1, vec![Instruction::Halt], Vec::new()),
8793 generator_function(
8794 0,
8795 1,
8796 vec![
8797 Instruction::LoadConst {
8798 dst: reg(0),
8799 constant: cid(0),
8800 },
8801 Instruction::Throw { value: reg(0) },
8802 ],
8803 Vec::new(),
8804 ),
8805 ],
8806 );
8807 let mut host = TestHost;
8808 let mut machine = Machine::new(&program, &mut host, Limits::default());
8809 machine.frames.clear();
8810 machine.live_registers = 0;
8811 let callable = generator_callable(&mut machine, 1);
8812 let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8813
8814 assert!(matches!(
8815 generator_next(&mut machine, generator, Value::UNDEFINED),
8816 Err(EvalFailure::ThrowValueOrigin {
8817 value,
8818 origin: ThrowOrigin::Bytecode,
8819 }) if value == Value::int32(7)
8820 ));
8821 assert_eq!(
8822 generator_next(&mut machine, generator, Value::UNDEFINED).unwrap(),
8823 (Value::UNDEFINED, true),
8824 );
8825 assert_eq!(machine.live_registers, 0);
8826 }
8827
8828 #[test]
8829 fn outer_compiled_handler_catches_generator_throw_value() {
8830 let program = verified(
8831 vec![
8832 Constant::Int32(7),
8833 Constant::Undefined,
8834 Constant::String(EcmaString::from_utf8("next")),
8835 ],
8836 vec![
8837 function(
8838 0,
8839 8,
8840 vec![
8841 Instruction::CreateArray { dst: reg(0) },
8842 Instruction::CreateClosure {
8843 dst: reg(1),
8844 function: FunctionId::new(1),
8845 captures: reg(0),
8846 },
8847 Instruction::CreateArray { dst: reg(2) },
8848 Instruction::LoadConst {
8849 dst: reg(3),
8850 constant: cid(1),
8851 },
8852 Instruction::Call {
8853 dst: reg(4),
8854 callee: reg(1),
8855 this_value: reg(3),
8856 arguments: reg(2),
8857 },
8858 Instruction::LoadConst {
8859 dst: reg(5),
8860 constant: cid(2),
8861 },
8862 Instruction::GetProperty {
8863 dst: reg(6),
8864 object: reg(4),
8865 key: reg(5),
8866 },
8867 Instruction::Call {
8868 dst: reg(7),
8869 callee: reg(6),
8870 this_value: reg(4),
8871 arguments: reg(2),
8872 },
8873 Instruction::Return { value: reg(3) },
8874 Instruction::Return { value: reg(7) },
8875 ],
8876 vec![ExceptionHandler {
8877 start: pc(7),
8878 end: pc(8),
8879 handler: pc(9),
8880 catch_register: reg(7),
8881 }],
8882 ),
8883 generator_function(
8884 0,
8885 1,
8886 vec![
8887 Instruction::LoadConst {
8888 dst: reg(0),
8889 constant: cid(0),
8890 },
8891 Instruction::Throw { value: reg(0) },
8892 ],
8893 Vec::new(),
8894 ),
8895 ],
8896 );
8897
8898 assert_eq!(run_ok(&program).value, Value::int32(7));
8899 }
8900
8901 #[test]
8902 fn sync_generator_catches_body_throw_before_suspending() {
8903 let program = verified(
8904 vec![Constant::Int32(7)],
8905 vec![
8906 function(0, 1, vec![Instruction::Halt], Vec::new()),
8907 generator_function(
8908 0,
8909 3,
8910 vec![
8911 Instruction::LoadConst {
8912 dst: reg(0),
8913 constant: cid(0),
8914 },
8915 Instruction::Throw { value: reg(0) },
8916 Instruction::Suspend {
8917 dst: reg(2),
8918 src: reg(1),
8919 resume: pc(3),
8920 },
8921 Instruction::Return { value: reg(2) },
8922 ],
8923 vec![ExceptionHandler {
8924 start: pc(1),
8925 end: pc(2),
8926 handler: pc(2),
8927 catch_register: reg(1),
8928 }],
8929 ),
8930 ],
8931 );
8932 let mut host = TestHost;
8933 let mut machine = Machine::new(&program, &mut host, Limits::default());
8934 machine.frames.clear();
8935 machine.live_registers = 0;
8936 let callable = generator_callable(&mut machine, 1);
8937 let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8938
8939 assert_eq!(
8940 generator_next(&mut machine, generator, Value::UNDEFINED).unwrap(),
8941 (Value::int32(7), false),
8942 );
8943 assert_eq!(
8944 generator_next(&mut machine, generator, Value::int32(9)).unwrap(),
8945 (Value::int32(9), true),
8946 );
8947 }
8948
8949 #[test]
8950 fn suspended_generator_registers_remain_charged() {
8951 let program = verified(
8952 vec![Constant::Int32(1)],
8953 vec![
8954 function(0, 1, vec![Instruction::Halt], Vec::new()),
8955 generator_function(
8956 0,
8957 3,
8958 vec![
8959 Instruction::LoadConst {
8960 dst: reg(0),
8961 constant: cid(0),
8962 },
8963 Instruction::Suspend {
8964 dst: reg(1),
8965 src: reg(0),
8966 resume: pc(2),
8967 },
8968 Instruction::Return { value: reg(1) },
8969 ],
8970 Vec::new(),
8971 ),
8972 ],
8973 );
8974 let mut host = TestHost;
8975 let mut machine = Machine::new(
8976 &program,
8977 &mut host,
8978 Limits {
8979 max_total_registers: 3,
8980 ..Limits::default()
8981 },
8982 );
8983 machine.frames.clear();
8984 machine.live_registers = 0;
8985 let callable = generator_callable(&mut machine, 1);
8986 let first = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8987 let second = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8988 assert_eq!(
8989 generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
8990 (Value::int32(1), false),
8991 );
8992 assert!(matches!(
8993 generator_next(&mut machine, second, Value::UNDEFINED),
8994 Err(EvalFailure::Runtime(
8995 RuntimeErrorKind::RegisterLimitExceeded { .. }
8996 ))
8997 ));
8998 assert_eq!(machine.live_registers, 3);
8999 assert_eq!(
9000 generator_next(&mut machine, first, Value::int32(4)).unwrap(),
9001 (Value::int32(4), true),
9002 );
9003 assert_eq!(machine.live_registers, 0);
9004 }
9005
9006 #[test]
9007 fn resumed_generator_call_depth_failure_releases_registers() {
9008 let program = verified(
9009 vec![Constant::Int32(1)],
9010 vec![
9011 function(0, 1, vec![Instruction::Halt], Vec::new()),
9012 generator_function(
9013 0,
9014 2,
9015 vec![
9016 Instruction::LoadConst {
9017 dst: reg(0),
9018 constant: cid(0),
9019 },
9020 Instruction::Suspend {
9021 dst: reg(1),
9022 src: reg(0),
9023 resume: pc(2),
9024 },
9025 Instruction::Return { value: reg(1) },
9026 ],
9027 Vec::new(),
9028 ),
9029 ],
9030 );
9031 let mut host = TestHost;
9032 let mut machine = Machine::new(
9033 &program,
9034 &mut host,
9035 Limits {
9036 max_total_registers: 2,
9037 ..Limits::default()
9038 },
9039 );
9040 machine.frames.clear();
9041 machine.live_registers = 0;
9042
9043 let callable = generator_callable(&mut machine, 1);
9044 let first = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
9045
9046 assert_eq!(
9048 generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
9049 (Value::int32(1), false),
9050 );
9051 assert_eq!(machine.live_registers, 2);
9052
9053 machine.frames.push(Frame {
9056 module: ModuleId::new(0),
9057 function: 0,
9058 pc: 0,
9059 registers: Vec::new(),
9060 return_to: None,
9061 this_value: Value::UNDEFINED,
9062 new_target: Value::UNDEFINED,
9063 args: Vec::new(),
9064 arguments_object: None,
9065 });
9066 machine.limits.max_call_depth = machine.frames.len();
9067
9068 assert!(matches!(
9069 generator_next(&mut machine, first, Value::int32(7)),
9070 Err(EvalFailure::Runtime(
9071 RuntimeErrorKind::CallDepthExceeded { .. }
9072 ))
9073 ));
9074 assert_eq!(machine.live_registers, 0);
9075
9076 assert_eq!(
9078 generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
9079 (Value::UNDEFINED, true),
9080 );
9081
9082 machine.frames.pop();
9084 machine.limits.max_call_depth = Limits::default().max_call_depth;
9085
9086 let second = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
9088 assert_eq!(
9089 generator_next(&mut machine, second, Value::UNDEFINED).unwrap(),
9090 (Value::int32(1), false),
9091 );
9092 assert_eq!(machine.live_registers, 2);
9093 assert_eq!(
9094 generator_next(&mut machine, second, Value::int32(9)).unwrap(),
9095 (Value::int32(9), true),
9096 );
9097 assert_eq!(machine.live_registers, 0);
9098 }
9099 #[test]
9100 fn array_extend_consumes_generator_through_sync_iterator_protocol() {
9101 let program = verified(
9102 vec![Constant::Int32(1), Constant::Int32(2)],
9103 vec![
9104 function(0, 1, vec![Instruction::Halt], Vec::new()),
9105 generator_function(
9106 0,
9107 3,
9108 vec![
9109 Instruction::LoadConst {
9110 dst: reg(0),
9111 constant: cid(0),
9112 },
9113 Instruction::Suspend {
9114 dst: reg(2),
9115 src: reg(0),
9116 resume: pc(2),
9117 },
9118 Instruction::LoadConst {
9119 dst: reg(1),
9120 constant: cid(1),
9121 },
9122 Instruction::Suspend {
9123 dst: reg(2),
9124 src: reg(1),
9125 resume: pc(4),
9126 },
9127 Instruction::Return { value: reg(2) },
9128 ],
9129 Vec::new(),
9130 ),
9131 ],
9132 );
9133 let mut host = TestHost;
9134 let mut machine = Machine::new(&program, &mut host, Limits::default());
9135 machine.frames.clear();
9136 machine.live_registers = 0;
9137 let callable = generator_callable(&mut machine, 1);
9138 let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
9139 let array = machine
9140 .allocate(HeapEntry::Array {
9141 elements: Vec::new(),
9142 properties: PropertyMap::default(),
9143 prototype: Some(machine.intrinsics.array_prototype),
9144 extensible: true,
9145 length_writable: true,
9146 })
9147 .unwrap();
9148
9149 machine.array_extend(array, generator).unwrap();
9150 assert_eq!(
9151 machine.array_elements(array).unwrap(),
9152 Some(vec![Value::int32(1), Value::int32(2)]),
9153 );
9154 assert_eq!(machine.live_registers, 0);
9155 }
9156
9157 #[test]
9158 fn runtime_callback_without_interpreter_caller_propagates_throw() {
9159 let program = verified(
9160 Vec::new(),
9161 vec![
9162 function(0, 1, vec![Instruction::Halt], Vec::new()),
9163 function(1, 1, vec![Instruction::Throw { value: reg(0) }], Vec::new()),
9164 ],
9165 );
9166 let mut host = TestHost;
9167 let mut machine = Machine::new(&program, &mut host, Limits::default());
9168 machine.frames.clear();
9169 machine.live_registers = 0;
9170 let callee = machine
9171 .allocate(HeapEntry::Function {
9172 module: ModuleId::new(0),
9173 function: FunctionId::new(1),
9174 captures: Vec::new(),
9175 properties: PropertyMap::default(),
9176 prototype: Some(machine.intrinsics.function_prototype),
9177 extensible: true,
9178 })
9179 .unwrap();
9180 let thrown = Value::int32(7);
9181
9182 assert!(matches!(
9183 machine.call_value(callee, Value::UNDEFINED, &[thrown]),
9184 Err(EvalFailure::ThrowValue(value)) if value == thrown
9185 ));
9186 }
9187
9188 #[test]
9189 fn runtime_callback_failure_releases_root_frame() {
9190 let program = verified(
9191 Vec::new(),
9192 vec![
9193 function(0, 1, vec![Instruction::Halt], Vec::new()),
9194 function(
9195 1,
9196 1,
9197 vec![Instruction::Return { value: reg(0) }],
9198 Vec::new(),
9199 ),
9200 ],
9201 );
9202 let mut host = TestHost;
9203 let mut machine = Machine::new(&program, &mut host, Limits::default());
9204 machine.frames.clear();
9205 machine.live_registers = 0;
9206 let callee = machine
9207 .allocate(HeapEntry::Function {
9208 module: ModuleId::new(0),
9209 function: FunctionId::new(1),
9210 captures: Vec::new(),
9211 properties: PropertyMap::default(),
9212 prototype: Some(machine.intrinsics.function_prototype),
9213 extensible: true,
9214 })
9215 .unwrap();
9216 machine.fuel = 0;
9217
9218 assert!(matches!(
9219 machine.call_value(callee, Value::UNDEFINED, &[Value::int32(7)]),
9220 Err(EvalFailure::Runtime(RuntimeErrorKind::FuelExhausted { .. }))
9221 ));
9222 assert!(machine.frames.is_empty());
9223 assert_eq!(machine.live_registers, 0);
9224
9225 machine.fuel = 1;
9226 assert!(matches!(
9227 machine.call_value(callee, Value::UNDEFINED, &[Value::int32(7)]),
9228 Ok(value) if value == Value::int32(7)
9229 ));
9230 }
9231
9232 #[test]
9233 fn object_values_have_stable_distinct_heap_identity() {
9234 let module = verified(
9235 vec![],
9236 vec![function(
9237 0,
9238 5,
9239 vec![
9240 Instruction::CreateObject { dst: reg(0) },
9241 Instruction::CreateObject { dst: reg(1) },
9242 Instruction::Binary {
9243 dst: reg(2),
9244 op: BinaryOp::StrictEqual,
9245 left: reg(0),
9246 right: reg(1),
9247 },
9248 Instruction::Move {
9249 dst: reg(3),
9250 src: reg(0),
9251 },
9252 Instruction::Binary {
9253 dst: reg(4),
9254 op: BinaryOp::StrictEqual,
9255 left: reg(0),
9256 right: reg(3),
9257 },
9258 Instruction::Return { value: reg(4) },
9259 ],
9260 vec![],
9261 )],
9262 );
9263 let execution = run_ok(&module);
9264 assert_eq!(execution.entry_registers[2], Value::FALSE);
9265 assert_eq!(execution.value, Value::TRUE);
9266 }
9267
9268 #[test]
9269 fn addition_coerces_objects_left_to_right_and_interpolates_errors() {
9270 let module = verified(
9271 vec![
9272 Constant::String(EcmaString::from_utf8("L")),
9273 Constant::String(EcmaString::from_utf8("additionOrder")),
9274 Constant::String(EcmaString::from_utf8("message")),
9275 ],
9276 vec![
9277 function(0, 1, vec![Instruction::Halt], Vec::new()),
9278 function(
9279 0,
9280 1,
9281 vec![
9282 Instruction::LoadConst {
9283 dst: reg(0),
9284 constant: cid(0),
9285 },
9286 Instruction::StoreGlobal {
9287 name: cid(1),
9288 value: reg(0),
9289 },
9290 Instruction::Return { value: reg(0) },
9291 ],
9292 Vec::new(),
9293 ),
9294 function(
9295 0,
9296 1,
9297 vec![
9298 Instruction::LoadGlobal {
9299 dst: reg(0),
9300 name: cid(1),
9301 },
9302 Instruction::Return { value: reg(0) },
9303 ],
9304 Vec::new(),
9305 ),
9306 ],
9307 );
9308 let mut host = TestHost;
9309 let mut machine = Machine::new(&module, &mut host, Limits::default());
9310 machine.frames.clear();
9311 machine.live_registers = 0;
9312 let left = machine
9313 .allocate(HeapEntry::Object {
9314 properties: PropertyMap::default(),
9315 prototype: Some(machine.intrinsics.object_prototype),
9316 extensible: true,
9317 boxed_primitive: None,
9318 })
9319 .unwrap();
9320 let right = machine
9321 .allocate(HeapEntry::Object {
9322 properties: PropertyMap::default(),
9323 prototype: Some(machine.intrinsics.object_prototype),
9324 extensible: true,
9325 boxed_primitive: None,
9326 })
9327 .unwrap();
9328 let left_value_of = machine
9329 .allocate(HeapEntry::Function {
9330 module: ModuleId::new(0),
9331 function: FunctionId::new(1),
9332 captures: Vec::new(),
9333 properties: PropertyMap::default(),
9334 prototype: Some(machine.intrinsics.function_prototype),
9335 extensible: true,
9336 })
9337 .unwrap();
9338 let right_value_of = machine
9339 .allocate(HeapEntry::Function {
9340 module: ModuleId::new(0),
9341 function: FunctionId::new(2),
9342 captures: Vec::new(),
9343 properties: PropertyMap::default(),
9344 prototype: Some(machine.intrinsics.function_prototype),
9345 extensible: true,
9346 })
9347 .unwrap();
9348 machine
9349 .set_data_property(left, "valueOf", left_value_of)
9350 .unwrap();
9351 machine
9352 .set_data_property(right, "valueOf", right_value_of)
9353 .unwrap();
9354 let coerced = machine.add(left, right).unwrap();
9355 assert!(
9356 machine
9357 .string_value(coerced)
9358 .is_some_and(|text| text.eq_ascii("LL"))
9359 );
9360
9361 let error_constructor = machine.intrinsics.global("Error").unwrap();
9362 let message = machine
9363 .allocate(HeapEntry::String(EcmaString::from_utf8("message")))
9364 .unwrap();
9365 let error = machine
9366 .call_value(error_constructor, Value::UNDEFINED, &[message])
9367 .unwrap();
9368 let empty = machine
9369 .allocate(HeapEntry::String(EcmaString::default()))
9370 .unwrap();
9371 let interpolated = machine.add(empty, error).unwrap();
9372 assert!(
9373 machine
9374 .string_value(interpolated)
9375 .is_some_and(|text| text.eq_ascii("Error: message"))
9376 );
9377
9378 let date_constructor = machine.intrinsics.global("Date").unwrap();
9379 let date_prototype = machine
9380 .get_named_property(date_constructor, "prototype")
9381 .unwrap();
9382 let date = machine
9383 .allocate(HeapEntry::Date {
9384 time: 0.0,
9385 properties: PropertyMap::default(),
9386 prototype: Some(date_prototype),
9387 extensible: true,
9388 })
9389 .unwrap();
9390 machine
9391 .set_data_property(date, "toString", left_value_of)
9392 .unwrap();
9393 let date_text = machine.add(date, empty).unwrap();
9394 assert!(
9395 machine
9396 .string_value(date_text)
9397 .is_some_and(|text| text.eq_ascii("L"))
9398 );
9399 }
9400
9401 #[test]
9402 fn computed_member_access_uses_dynamic_register_key() {
9403 let module = verified(
9405 vec![
9406 Constant::String(EcmaString::from_utf8("a")),
9407 Constant::String(EcmaString::from_utf8("b")),
9408 Constant::Int32(7),
9409 ],
9410 vec![function(
9411 0,
9412 6,
9413 vec![
9414 Instruction::LoadConst {
9415 dst: reg(1),
9416 constant: cid(0),
9417 },
9418 Instruction::LoadConst {
9419 dst: reg(2),
9420 constant: cid(1),
9421 },
9422 Instruction::Binary {
9423 dst: reg(3),
9424 op: BinaryOp::Add,
9425 left: reg(1),
9426 right: reg(2),
9427 },
9428 Instruction::CreateObject { dst: reg(0) },
9429 Instruction::LoadConst {
9430 dst: reg(4),
9431 constant: cid(2),
9432 },
9433 Instruction::SetProperty {
9434 object: reg(0),
9435 key: reg(3),
9436 value: reg(4),
9437 },
9438 Instruction::GetProperty {
9439 dst: reg(5),
9440 object: reg(0),
9441 key: reg(3),
9442 },
9443 Instruction::Return { value: reg(5) },
9444 ],
9445 vec![],
9446 )],
9447 );
9448 assert_eq!(run_ok(&module).value, Value::int32(7));
9449 }
9450
9451 #[test]
9452 fn property_delete_and_array_holes_are_real_mutations() {
9453 let module = verified(
9454 vec![
9455 Constant::String(EcmaString::from_utf8("0")),
9456 Constant::Int32(5),
9457 ],
9458 vec![function(
9459 0,
9460 5,
9461 vec![
9462 Instruction::CreateArray { dst: reg(0) },
9463 Instruction::LoadConst {
9464 dst: reg(1),
9465 constant: cid(0),
9466 },
9467 Instruction::LoadConst {
9468 dst: reg(4),
9469 constant: cid(1),
9470 },
9471 Instruction::SetProperty {
9472 object: reg(0),
9473 key: reg(1),
9474 value: reg(4),
9475 },
9476 Instruction::GetProperty {
9477 dst: reg(2),
9478 object: reg(0),
9479 key: reg(1),
9480 },
9481 Instruction::DeleteProperty {
9482 dst: reg(3),
9483 object: reg(0),
9484 key: reg(1),
9485 },
9486 Instruction::GetProperty {
9487 dst: reg(4),
9488 object: reg(0),
9489 key: reg(1),
9490 },
9491 Instruction::Return { value: reg(3) },
9492 ],
9493 vec![],
9494 )],
9495 );
9496 let execution = run_ok(&module);
9497 assert_eq!(execution.entry_registers[2], Value::int32(5));
9498 assert_eq!(execution.entry_registers[4], Value::UNDEFINED);
9499 assert_eq!(execution.value, Value::TRUE);
9500 }
9501
9502 #[test]
9503 fn closure_captures_seed_leading_registers_before_parameters() {
9504 let entry = function(
9506 0,
9507 3,
9508 vec![
9509 Instruction::CreateArray { dst: reg(0) },
9510 Instruction::LoadConst {
9511 dst: reg(1),
9512 constant: cid(0),
9513 },
9514 Instruction::ArrayPush {
9515 array: reg(0),
9516 value: reg(1),
9517 },
9518 Instruction::CreateClosure {
9519 dst: reg(2),
9520 function: FunctionId::new(1),
9521 captures: reg(0),
9522 },
9523 Instruction::CreateArray { dst: reg(0) },
9525 Instruction::LoadConst {
9526 dst: reg(1),
9527 constant: cid(1),
9528 },
9529 Instruction::ArrayPush {
9530 array: reg(0),
9531 value: reg(1),
9532 },
9533 Instruction::LoadConst {
9534 dst: reg(1),
9535 constant: cid(2),
9536 },
9537 Instruction::Call {
9538 dst: reg(1),
9539 callee: reg(2),
9540 this_value: reg(1),
9541 arguments: reg(0),
9542 },
9543 Instruction::Return { value: reg(1) },
9544 ],
9545 vec![],
9546 );
9547 let callee = closure_function(
9549 1,
9550 1,
9551 3,
9552 vec![
9553 Instruction::Binary {
9554 dst: reg(2),
9555 op: BinaryOp::Add,
9556 left: reg(0),
9557 right: reg(1),
9558 },
9559 Instruction::Return { value: reg(2) },
9560 ],
9561 );
9562 let module = verified(
9563 vec![Constant::Int32(42), Constant::Int32(7), Constant::Undefined],
9564 vec![entry, callee],
9565 );
9566 assert_eq!(run_ok(&module).value, Value::int32(49));
9567 }
9568
9569 #[test]
9570 fn calls_scale_past_fixed_window_via_arguments_array() {
9571 let mut code = vec![Instruction::CreateArray { dst: reg(0) }];
9574 code.push(Instruction::LoadConst {
9575 dst: reg(1),
9576 constant: cid(0),
9577 });
9578 for _ in 0..500 {
9579 code.push(Instruction::ArrayPush {
9580 array: reg(0),
9581 value: reg(1),
9582 });
9583 }
9584 code.push(Instruction::CreateClosure {
9585 dst: reg(2),
9586 function: FunctionId::new(1),
9587 captures: reg(3),
9588 });
9589 let mut prelude = vec![Instruction::CreateArray { dst: reg(3) }];
9593 prelude.append(&mut code);
9594 let mut code = prelude;
9595 code.push(Instruction::LoadConst {
9596 dst: reg(1),
9597 constant: cid(1),
9598 });
9599 code.push(Instruction::Call {
9600 dst: reg(1),
9601 callee: reg(2),
9602 this_value: reg(1),
9603 arguments: reg(0),
9604 });
9605 code.push(Instruction::Return { value: reg(1) });
9606
9607 let entry = function(0, 4, code, vec![]);
9608 let callee = function(
9609 0,
9610 2,
9611 vec![
9612 Instruction::LoadArguments { dst: reg(0) },
9613 Instruction::LoadConst {
9614 dst: reg(1),
9615 constant: cid(2),
9616 },
9617 Instruction::GetProperty {
9618 dst: reg(0),
9619 object: reg(0),
9620 key: reg(1),
9621 },
9622 Instruction::Return { value: reg(0) },
9623 ],
9624 vec![],
9625 );
9626 let module = verified(
9627 vec![
9628 Constant::Int32(1),
9629 Constant::Undefined,
9630 Constant::String(EcmaString::from_utf8("length")),
9631 ],
9632 vec![entry, callee],
9633 );
9634 assert_eq!(run_ok(&module).value, Value::int32(500));
9635 }
9636
9637 #[test]
9638 fn array_extend_spreads_iterable_elements() {
9639 let entry = function(
9641 0,
9642 4,
9643 vec![
9644 Instruction::CreateArray { dst: reg(0) },
9645 Instruction::LoadConst {
9646 dst: reg(1),
9647 constant: cid(0),
9648 },
9649 Instruction::ArrayPush {
9650 array: reg(0),
9651 value: reg(1),
9652 },
9653 Instruction::CreateArray { dst: reg(2) },
9655 Instruction::LoadConst {
9656 dst: reg(1),
9657 constant: cid(1),
9658 },
9659 Instruction::ArrayPush {
9660 array: reg(2),
9661 value: reg(1),
9662 },
9663 Instruction::LoadConst {
9664 dst: reg(1),
9665 constant: cid(2),
9666 },
9667 Instruction::ArrayPush {
9668 array: reg(2),
9669 value: reg(1),
9670 },
9671 Instruction::ArrayExtend {
9672 array: reg(0),
9673 iterable: reg(2),
9674 },
9675 Instruction::LoadConst {
9676 dst: reg(3),
9677 constant: cid(3),
9678 },
9679 Instruction::GetProperty {
9680 dst: reg(0),
9681 object: reg(0),
9682 key: reg(3),
9683 },
9684 Instruction::Return { value: reg(0) },
9685 ],
9686 vec![],
9687 );
9688 let module = verified(
9689 vec![
9690 Constant::Int32(1),
9691 Constant::Int32(2),
9692 Constant::Int32(3),
9693 Constant::String(EcmaString::from_utf8("length")),
9694 ],
9695 vec![entry],
9696 );
9697 assert_eq!(run_ok(&module).value, Value::int32(3));
9698 }
9699
9700 #[test]
9701 fn array_extend_uses_sync_protocol_for_set_and_rejects_plain_object() {
9702 let module = verified(
9703 Vec::new(),
9704 vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
9705 );
9706 let mut host = TestHost;
9707 let mut machine = Machine::new(&module, &mut host, Limits::default());
9708 let set_constructor = machine.intrinsics.global("Set").unwrap();
9709 let set_prototype = machine
9710 .get_named_property(set_constructor, "prototype")
9711 .unwrap();
9712 let set = machine
9713 .allocate(HeapEntry::Collection {
9714 entries: vec![CollectionEntry {
9715 order: 0,
9716 key: Value::int32(7),
9717 value: Value::int32(7),
9718 }],
9719 next_order: 1,
9720 properties: PropertyMap::default(),
9721 prototype: Some(set_prototype),
9722 extensible: true,
9723 })
9724 .unwrap();
9725 let target = machine
9726 .allocate(HeapEntry::Array {
9727 elements: Vec::new(),
9728 properties: PropertyMap::default(),
9729 prototype: Some(machine.intrinsics.array_prototype),
9730 extensible: true,
9731 length_writable: true,
9732 })
9733 .unwrap();
9734
9735 machine.array_extend(target, set).unwrap();
9736 assert_eq!(
9737 machine.array_elements(target).unwrap(),
9738 Some(vec![Value::int32(7)])
9739 );
9740
9741 let plain_object = machine
9742 .allocate(HeapEntry::Object {
9743 properties: PropertyMap::default(),
9744 prototype: Some(machine.intrinsics.object_prototype),
9745 boxed_primitive: None,
9746 extensible: true,
9747 })
9748 .unwrap();
9749 assert!(matches!(
9750 machine.array_extend(target, plain_object),
9751 Err(EvalFailure::Throw(ThrowOrigin::TypeError {
9752 operation: "value is not iterable"
9753 }))
9754 ));
9755 }
9756
9757 #[test]
9758 fn sync_iterator_uses_symbol_method_and_caches_next() {
9759 fn iterator_identity<H: Host>(
9760 _machine: &mut Machine<'_, H>,
9761 this: Value,
9762 _args: &[Value],
9763 _constructing: bool,
9764 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9765 Ok(intrinsics::BuiltinOutcome::Value(this))
9766 }
9767
9768 fn next_getter<H: Host>(
9769 machine: &mut Machine<'_, H>,
9770 this: Value,
9771 _args: &[Value],
9772 _constructing: bool,
9773 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9774 let reads = machine.get_named_property(this, "nextReads")?;
9775 let reads = if reads == Value::int32(0) { 1 } else { 2 };
9776 machine.set_data_property(this, "nextReads", Value::int32(reads))?;
9777 Ok(intrinsics::BuiltinOutcome::Value(
9778 machine.get_named_property(this, "nextFunction")?,
9779 ))
9780 }
9781
9782 fn next_result<H: Host>(
9783 machine: &mut Machine<'_, H>,
9784 this: Value,
9785 _args: &[Value],
9786 _constructing: bool,
9787 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9788 Ok(intrinsics::BuiltinOutcome::Value(
9789 machine.get_named_property(this, "result")?,
9790 ))
9791 }
9792
9793 fn done_getter<H: Host>(
9794 machine: &mut Machine<'_, H>,
9795 this: Value,
9796 _args: &[Value],
9797 _constructing: bool,
9798 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9799 machine.set_data_property(this, "order", Value::int32(1))?;
9800 Ok(intrinsics::BuiltinOutcome::Value(Value::FALSE))
9801 }
9802
9803 fn value_getter<H: Host>(
9804 machine: &mut Machine<'_, H>,
9805 this: Value,
9806 _args: &[Value],
9807 _constructing: bool,
9808 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9809 if machine.get_named_property(this, "order")? != Value::int32(1) {
9810 return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
9811 operation: "iterator value read before done",
9812 }));
9813 }
9814 Ok(intrinsics::BuiltinOutcome::Value(Value::int32(42)))
9815 }
9816
9817 let module = verified(
9818 Vec::new(),
9819 vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
9820 );
9821 let mut host = TestHost;
9822 let mut machine = Machine::new(&module, &mut host, Limits::default());
9823 let mut install = |name, handler| {
9824 let id = machine
9825 .intrinsics
9826 .builtins
9827 .register(intrinsics::BuiltinDef {
9828 name,
9829 length: 0,
9830 handler,
9831 });
9832 intrinsics::native_function(&mut machine.heap, id, name, 0)
9833 };
9834 let iterator_identity = install(
9835 "[Symbol.iterator]",
9836 iterator_identity::<TestHost> as intrinsics::BuiltinHandler<TestHost>,
9837 );
9838 let next_getter = install("get next", next_getter::<TestHost>);
9839 let next_result = install("next", next_result::<TestHost>);
9840 let done_getter = install("get done", done_getter::<TestHost>);
9841 let value_getter = install("get value", value_getter::<TestHost>);
9842 let object_prototype = machine.intrinsics.object_prototype;
9843 let result = machine
9844 .allocate(HeapEntry::Object {
9845 properties: {
9846 let mut properties = PropertyMap::default();
9847 for (key, property) in [
9848 (
9849 PropertyKey::Named(EcmaString::from_utf8("order")),
9850 Property::Data {
9851 value: Value::int32(0),
9852 writable: true,
9853 enumerable: true,
9854 configurable: true,
9855 },
9856 ),
9857 (
9858 PropertyKey::Named(EcmaString::from_utf8("done")),
9859 Property::Accessor {
9860 getter: Some(done_getter),
9861 setter: None,
9862 enumerable: true,
9863 configurable: true,
9864 },
9865 ),
9866 (
9867 PropertyKey::Named(EcmaString::from_utf8("value")),
9868 Property::Accessor {
9869 getter: Some(value_getter),
9870 setter: None,
9871 enumerable: true,
9872 configurable: true,
9873 },
9874 ),
9875 ] {
9876 properties.insert(key, property);
9877 }
9878 properties
9879 },
9880 prototype: Some(object_prototype),
9881 boxed_primitive: None,
9882 extensible: true,
9883 })
9884 .unwrap();
9885 let iterator_symbol = machine.intrinsics.builtins.symbol_iterator();
9886 let iterator_key = machine.to_property_key(iterator_symbol).unwrap();
9887 let source = machine
9888 .allocate(HeapEntry::Object {
9889 properties: {
9890 let mut properties = PropertyMap::default();
9891 for (key, property) in [
9892 (
9893 iterator_key,
9894 Property::Data {
9895 value: iterator_identity,
9896 writable: true,
9897 enumerable: false,
9898 configurable: true,
9899 },
9900 ),
9901 (
9902 PropertyKey::Named(EcmaString::from_utf8("next")),
9903 Property::Accessor {
9904 getter: Some(next_getter),
9905 setter: None,
9906 enumerable: false,
9907 configurable: true,
9908 },
9909 ),
9910 (
9911 PropertyKey::Named(EcmaString::from_utf8("nextReads")),
9912 Property::Data {
9913 value: Value::int32(0),
9914 writable: true,
9915 enumerable: true,
9916 configurable: true,
9917 },
9918 ),
9919 (
9920 PropertyKey::Named(EcmaString::from_utf8("nextFunction")),
9921 Property::Data {
9922 value: next_result,
9923 writable: true,
9924 enumerable: true,
9925 configurable: true,
9926 },
9927 ),
9928 (
9929 PropertyKey::Named(EcmaString::from_utf8("result")),
9930 Property::Data {
9931 value: result,
9932 writable: true,
9933 enumerable: true,
9934 configurable: true,
9935 },
9936 ),
9937 ] {
9938 properties.insert(key, property);
9939 }
9940 properties
9941 },
9942 prototype: Some(object_prototype),
9943 boxed_primitive: None,
9944 extensible: true,
9945 })
9946 .unwrap();
9947
9948 let iterator = machine.create_iterator(source, IteratorKind::Sync).unwrap();
9949 assert_eq!(
9950 machine.iterator_next(iterator).unwrap(),
9951 (false, Value::int32(42))
9952 );
9953 assert_eq!(
9954 machine.iterator_next(iterator).unwrap(),
9955 (false, Value::int32(42))
9956 );
9957 assert_eq!(
9958 machine.get_named_property(source, "nextReads").unwrap(),
9959 Value::int32(1)
9960 );
9961
9962 let mut completed_properties = PropertyMap::default();
9963 completed_properties.insert(
9964 PropertyKey::Named(EcmaString::from_utf8("done")),
9965 Property::Data {
9966 value: Value::TRUE,
9967 writable: true,
9968 enumerable: true,
9969 configurable: true,
9970 },
9971 );
9972 completed_properties.insert(
9973 PropertyKey::Named(EcmaString::from_utf8("value")),
9974 Property::Accessor {
9975 getter: Some(value_getter),
9976 setter: None,
9977 enumerable: true,
9978 configurable: true,
9979 },
9980 );
9981 let completed = machine
9982 .allocate(HeapEntry::Object {
9983 properties: completed_properties,
9984 prototype: Some(object_prototype),
9985 boxed_primitive: None,
9986 extensible: true,
9987 })
9988 .unwrap();
9989 machine
9990 .set_data_property(source, "result", completed)
9991 .unwrap();
9992 assert_eq!(
9993 machine.iterator_next(iterator).unwrap(),
9994 (true, Value::UNDEFINED)
9995 );
9996
9997 machine
9998 .delete_property(source, &PropertyKey::Named(EcmaString::from_utf8("next")))
9999 .unwrap();
10000 machine
10001 .set_data_property(source, "next", Value::int32(1))
10002 .unwrap();
10003 let invalid_next = machine.create_iterator(source, IteratorKind::Sync).unwrap();
10004 assert!(matches!(
10005 machine.iterator_next(invalid_next),
10006 Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
10007 ));
10008 }
10009
10010 #[test]
10011 fn object_spread_copies_own_properties() {
10012 let key = |c: u32| Instruction::LoadConst {
10014 dst: reg(3),
10015 constant: cid(c),
10016 };
10017 let module = verified(
10018 vec![
10019 Constant::String(EcmaString::from_utf8("x")),
10020 Constant::Int32(9),
10021 ],
10022 vec![function(
10023 0,
10024 4,
10025 vec![
10026 Instruction::CreateObject { dst: reg(0) },
10027 key(0),
10028 Instruction::LoadConst {
10029 dst: reg(2),
10030 constant: cid(1),
10031 },
10032 Instruction::SetProperty {
10033 object: reg(0),
10034 key: reg(3),
10035 value: reg(2),
10036 },
10037 Instruction::CreateObject { dst: reg(1) },
10038 Instruction::ObjectSpread {
10039 target: reg(1),
10040 source: reg(0),
10041 },
10042 key(0),
10043 Instruction::GetProperty {
10044 dst: reg(2),
10045 object: reg(1),
10046 key: reg(3),
10047 },
10048 Instruction::Return { value: reg(2) },
10049 ],
10050 vec![],
10051 )],
10052 );
10053 assert_eq!(run_ok(&module).value, Value::int32(9));
10054 }
10055
10056 #[test]
10057 fn object_spread_copies_enumerable_symbol_properties() {
10058 let module = verified(
10059 Vec::new(),
10060 vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
10061 );
10062 let mut host = TestHost;
10063 let mut machine = Machine::new(&module, &mut host, Limits::default());
10064 let prototype = machine.intrinsics.object_prototype;
10065 let object = |machine: &mut Machine<'_, TestHost>| {
10066 machine
10067 .allocate(HeapEntry::Object {
10068 properties: PropertyMap::default(),
10069 prototype: Some(prototype),
10070 boxed_primitive: None,
10071 extensible: true,
10072 })
10073 .unwrap()
10074 };
10075 let source = object(&mut machine);
10076 let target = object(&mut machine);
10077 let symbol = machine
10078 .allocate(HeapEntry::Symbol {
10079 description: EcmaString::from_utf8("key"),
10080 })
10081 .unwrap();
10082 let key = machine.to_property_key(symbol).unwrap();
10083 machine
10084 .set_data_property_key(source, key.clone(), Value::int32(42))
10085 .unwrap();
10086
10087 machine.object_spread(target, source).unwrap();
10088
10089 assert_eq!(
10090 machine.get_property_key(target, &key).unwrap(),
10091 Value::int32(42)
10092 );
10093 }
10094
10095 #[test]
10096 fn object_spread_rechecks_descriptors_after_getters() {
10097 fn delete_next<H: Host>(
10098 machine: &mut Machine<'_, H>,
10099 this: Value,
10100 _args: &[Value],
10101 _constructing: bool,
10102 ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
10103 machine.delete_property(this, &PropertyKey::Named(EcmaString::from_utf8("next")))?;
10104 Ok(intrinsics::BuiltinOutcome::Value(Value::int32(1)))
10105 }
10106
10107 let module = verified(
10108 Vec::new(),
10109 vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
10110 );
10111 let mut host = TestHost;
10112 let mut machine = Machine::new(&module, &mut host, Limits::default());
10113 let getter_id = machine
10114 .intrinsics
10115 .builtins
10116 .register(intrinsics::BuiltinDef {
10117 name: "delete next",
10118 length: 0,
10119 handler: delete_next::<TestHost>,
10120 });
10121 let getter = intrinsics::native_function(&mut machine.heap, getter_id, "delete next", 0);
10122 let first = PropertyKey::Named(EcmaString::from_utf8("first"));
10123 let next = PropertyKey::Named(EcmaString::from_utf8("next"));
10124 let mut source_properties = PropertyMap::default();
10125 source_properties.insert(
10126 first.clone(),
10127 Property::Accessor {
10128 getter: Some(getter),
10129 setter: None,
10130 enumerable: true,
10131 configurable: true,
10132 },
10133 );
10134 source_properties.insert(
10135 next.clone(),
10136 Property::Data {
10137 value: Value::int32(2),
10138 writable: true,
10139 enumerable: true,
10140 configurable: true,
10141 },
10142 );
10143 let prototype = machine.intrinsics.object_prototype;
10144 let source = machine
10145 .allocate(HeapEntry::Object {
10146 properties: source_properties,
10147 prototype: Some(prototype),
10148 boxed_primitive: None,
10149 extensible: true,
10150 })
10151 .unwrap();
10152 let target = machine
10153 .allocate(HeapEntry::Object {
10154 properties: PropertyMap::default(),
10155 prototype: Some(prototype),
10156 boxed_primitive: None,
10157 extensible: true,
10158 })
10159 .unwrap();
10160
10161 machine.object_spread(target, source).unwrap();
10162
10163 assert_eq!(
10164 machine.get_property_key(target, &first).unwrap(),
10165 Value::int32(1)
10166 );
10167 assert!(!machine.has_own_property_key(target, &next).unwrap());
10168 }
10169
10170 #[test]
10171 fn private_names_have_distinct_identity_and_are_gettable() {
10172 let module = verified(
10174 vec![
10175 Constant::String(EcmaString::from_utf8("x")),
10176 Constant::Int32(1),
10177 Constant::Int32(2),
10178 ],
10179 vec![function(
10180 0,
10181 6,
10182 vec![
10183 Instruction::CreateObject { dst: reg(0) },
10184 Instruction::CreatePrivateName {
10185 dst: reg(1),
10186 description: cid(0),
10187 },
10188 Instruction::CreatePrivateName {
10189 dst: reg(2),
10190 description: cid(0),
10191 },
10192 Instruction::LoadConst {
10193 dst: reg(3),
10194 constant: cid(1),
10195 },
10196 Instruction::SetProperty {
10197 object: reg(0),
10198 key: reg(1),
10199 value: reg(3),
10200 },
10201 Instruction::LoadConst {
10202 dst: reg(3),
10203 constant: cid(2),
10204 },
10205 Instruction::SetProperty {
10206 object: reg(0),
10207 key: reg(2),
10208 value: reg(3),
10209 },
10210 Instruction::GetProperty {
10212 dst: reg(4),
10213 object: reg(0),
10214 key: reg(1),
10215 },
10216 Instruction::GetProperty {
10217 dst: reg(5),
10218 object: reg(0),
10219 key: reg(2),
10220 },
10221 Instruction::Binary {
10223 dst: reg(3),
10224 op: BinaryOp::StrictEqual,
10225 left: reg(1),
10226 right: reg(2),
10227 },
10228 Instruction::Return { value: reg(4) },
10229 ],
10230 vec![],
10231 )],
10232 );
10233 let execution = run_ok(&module);
10234 assert_eq!(execution.value, Value::int32(1));
10235 assert_eq!(execution.entry_registers[5], Value::int32(2));
10236 assert_eq!(execution.entry_registers[3], Value::FALSE);
10237 }
10238
10239 #[test]
10240 fn accessor_getter_is_invoked_on_property_read() {
10241 let entry = function(
10243 0,
10244 4,
10245 vec![
10246 Instruction::CreateObject { dst: reg(0) },
10247 Instruction::CreateArray { dst: reg(3) },
10248 Instruction::CreateClosure {
10249 dst: reg(1),
10250 function: FunctionId::new(1),
10251 captures: reg(3),
10252 },
10253 Instruction::LoadConst {
10254 dst: reg(2),
10255 constant: cid(0),
10256 },
10257 Instruction::DefineAccessor {
10258 object: reg(0),
10259 key: reg(2),
10260 accessor: reg(1),
10261 kind: AccessorKind::Getter,
10262 },
10263 Instruction::GetProperty {
10264 dst: reg(1),
10265 object: reg(0),
10266 key: reg(2),
10267 },
10268 Instruction::Return { value: reg(1) },
10269 ],
10270 vec![],
10271 );
10272 let getter = function(
10273 0,
10274 1,
10275 vec![
10276 Instruction::LoadConst {
10277 dst: reg(0),
10278 constant: cid(1),
10279 },
10280 Instruction::Return { value: reg(0) },
10281 ],
10282 vec![],
10283 );
10284 let module = verified(
10285 vec![
10286 Constant::String(EcmaString::from_utf8("g")),
10287 Constant::Int32(99),
10288 ],
10289 vec![entry, getter],
10290 );
10291 assert_eq!(run_ok(&module).value, Value::int32(99));
10292 }
10293
10294 #[test]
10295 fn prototype_chain_lookup_and_instanceof() {
10296 let entry = function(
10299 0,
10300 6,
10301 vec![
10302 Instruction::CreateObject { dst: reg(0) },
10304 Instruction::LoadConst {
10305 dst: reg(1),
10306 constant: cid(0),
10307 },
10308 Instruction::LoadConst {
10309 dst: reg(2),
10310 constant: cid(1),
10311 },
10312 Instruction::SetProperty {
10313 object: reg(0),
10314 key: reg(1),
10315 value: reg(2),
10316 },
10317 Instruction::CreateArray { dst: reg(4) },
10319 Instruction::CreateClosure {
10320 dst: reg(3),
10321 function: FunctionId::new(1),
10322 captures: reg(4),
10323 },
10324 Instruction::LoadConst {
10326 dst: reg(1),
10327 constant: cid(2),
10328 },
10329 Instruction::SetProperty {
10330 object: reg(3),
10331 key: reg(1),
10332 value: reg(0),
10333 },
10334 Instruction::CreateArray { dst: reg(4) },
10336 Instruction::Construct {
10337 dst: reg(0),
10338 callee: reg(3),
10339 arguments: reg(4),
10340 },
10341 Instruction::LoadConst {
10343 dst: reg(1),
10344 constant: cid(0),
10345 },
10346 Instruction::GetProperty {
10347 dst: reg(2),
10348 object: reg(0),
10349 key: reg(1),
10350 },
10351 Instruction::Binary {
10353 dst: reg(5),
10354 op: BinaryOp::InstanceOf,
10355 left: reg(0),
10356 right: reg(3),
10357 },
10358 Instruction::Return { value: reg(2) },
10359 ],
10360 vec![],
10361 );
10362 let ctor = function(0, 1, vec![Instruction::Halt], vec![]);
10363 let module = verified(
10364 vec![
10365 Constant::String(EcmaString::from_utf8("m")),
10366 Constant::Int32(5),
10367 Constant::String(EcmaString::from_utf8("prototype")),
10368 ],
10369 vec![entry, ctor],
10370 );
10371 let execution = run_ok(&module);
10372 assert_eq!(execution.value, Value::int32(5));
10373 assert_eq!(execution.entry_registers[5], Value::TRUE);
10374 }
10375
10376 #[test]
10377 fn sync_iterator_walks_array_elements() {
10378 let entry = function(
10380 0,
10381 6,
10382 vec![
10383 Instruction::CreateArray { dst: reg(0) },
10384 Instruction::LoadConst {
10385 dst: reg(1),
10386 constant: cid(0),
10387 },
10388 Instruction::ArrayPush {
10389 array: reg(0),
10390 value: reg(1),
10391 },
10392 Instruction::LoadConst {
10393 dst: reg(1),
10394 constant: cid(1),
10395 },
10396 Instruction::ArrayPush {
10397 array: reg(0),
10398 value: reg(1),
10399 },
10400 Instruction::LoadConst {
10402 dst: reg(2),
10403 constant: cid(2),
10404 },
10405 Instruction::GetIterator {
10406 dst: reg(3),
10407 src: reg(0),
10408 kind: IteratorKind::Sync,
10409 },
10410 Instruction::IteratorNext {
10412 done: reg(4),
10413 value: reg(5),
10414 iterator: reg(3),
10415 },
10416 Instruction::JumpIfTrue {
10417 condition: reg(4),
10418 target: pc(11),
10419 },
10420 Instruction::Binary {
10421 dst: reg(2),
10422 op: BinaryOp::Add,
10423 left: reg(2),
10424 right: reg(5),
10425 },
10426 Instruction::Jump { target: pc(7) },
10427 Instruction::Return { value: reg(2) },
10429 ],
10430 vec![],
10431 );
10432 let module = verified(
10433 vec![Constant::Int32(10), Constant::Int32(20), Constant::Int32(0)],
10434 vec![entry],
10435 );
10436 assert_eq!(run_ok(&module).value, Value::int32(30));
10437 }
10438
10439 #[test]
10440 fn keys_iterator_enumerates_own_object_keys() {
10441 let entry = function(
10443 0,
10444 6,
10445 vec![
10446 Instruction::CreateObject { dst: reg(0) },
10447 Instruction::LoadConst {
10448 dst: reg(1),
10449 constant: cid(0),
10450 },
10451 Instruction::LoadConst {
10452 dst: reg(2),
10453 constant: cid(1),
10454 },
10455 Instruction::SetProperty {
10456 object: reg(0),
10457 key: reg(1),
10458 value: reg(2),
10459 },
10460 Instruction::GetIterator {
10461 dst: reg(3),
10462 src: reg(0),
10463 kind: IteratorKind::Keys,
10464 },
10465 Instruction::IteratorNext {
10466 done: reg(4),
10467 value: reg(5),
10468 iterator: reg(3),
10469 },
10470 Instruction::Return { value: reg(5) },
10471 ],
10472 vec![],
10473 );
10474 let module = verified(
10475 vec![
10476 Constant::String(EcmaString::from_utf8("a")),
10477 Constant::Int32(1),
10478 ],
10479 vec![entry],
10480 );
10481 let execution = run_ok(&module);
10482 let key = execution.value;
10484 assert_eq!(execution.entry_registers[4], Value::FALSE);
10487 assert_ne!(key, Value::UNDEFINED);
10488 }
10489
10490 #[test]
10491 fn async_iterator_steps_like_sync() {
10492 let entry = function(
10493 0,
10494 5,
10495 vec![
10496 Instruction::CreateArray { dst: reg(0) },
10497 Instruction::LoadConst {
10498 dst: reg(1),
10499 constant: cid(0),
10500 },
10501 Instruction::ArrayPush {
10502 array: reg(0),
10503 value: reg(1),
10504 },
10505 Instruction::GetIterator {
10506 dst: reg(2),
10507 src: reg(0),
10508 kind: IteratorKind::Async,
10509 },
10510 Instruction::IteratorNext {
10511 done: reg(3),
10512 value: reg(4),
10513 iterator: reg(2),
10514 },
10515 Instruction::Return { value: reg(4) },
10516 ],
10517 vec![],
10518 );
10519 let module = verified(vec![Constant::Int32(8)], vec![entry]);
10520 let execution = run_ok(&module);
10521 assert_eq!(execution.value, Value::int32(8));
10522 assert_eq!(execution.entry_registers[3], Value::FALSE);
10523 }
10524
10525 #[test]
10526 fn globals_store_load_and_typeof_undeclared() {
10527 let entry = function(
10530 0,
10531 3,
10532 vec![
10533 Instruction::LoadConst {
10534 dst: reg(0),
10535 constant: cid(2),
10536 },
10537 Instruction::StoreGlobal {
10538 name: cid(0),
10539 value: reg(0),
10540 },
10541 Instruction::TypeOfGlobal {
10542 dst: reg(1),
10543 name: cid(1),
10544 },
10545 Instruction::TypeOfGlobal {
10546 dst: reg(2),
10547 name: cid(0),
10548 },
10549 Instruction::LoadGlobal {
10550 dst: reg(0),
10551 name: cid(0),
10552 },
10553 Instruction::Return { value: reg(0) },
10554 ],
10555 vec![],
10556 );
10557 let module = verified(
10558 vec![
10559 Constant::String(EcmaString::from_utf8("x")),
10560 Constant::String(EcmaString::from_utf8("y")),
10561 Constant::Int32(5),
10562 ],
10563 vec![entry],
10564 );
10565 assert_eq!(run_ok(&module).value, Value::int32(5));
10566 }
10567
10568 #[test]
10569 fn create_cell_throws_reference_error_before_initialization() {
10570 let module = verified(
10571 vec![Constant::Int32(0)],
10572 vec![function(
10573 0,
10574 3,
10575 vec![
10576 Instruction::CreateCell { dst: reg(0) },
10577 Instruction::LoadConst {
10578 dst: reg(1),
10579 constant: cid(0),
10580 },
10581 Instruction::GetProperty {
10582 dst: reg(2),
10583 object: reg(0),
10584 key: reg(1),
10585 },
10586 Instruction::Return { value: reg(2) },
10587 ],
10588 vec![],
10589 )],
10590 );
10591 let mut host = TestHost;
10592 let error = Machine::new(&module, &mut host, Limits::default())
10593 .run()
10594 .expect_err("uninitialized cell read throws");
10595 assert!(matches!(
10596 error.kind,
10597 RuntimeErrorKind::UncaughtThrow {
10598 origin: ThrowOrigin::ReferenceError { .. },
10599 ..
10600 }
10601 ));
10602 }
10603
10604 #[test]
10605 fn create_cell_can_be_initialized_to_undefined() {
10606 let module = verified(
10607 vec![Constant::Int32(0), Constant::Undefined],
10608 vec![function(
10609 0,
10610 4,
10611 vec![
10612 Instruction::CreateCell { dst: reg(0) },
10613 Instruction::LoadConst {
10614 dst: reg(1),
10615 constant: cid(0),
10616 },
10617 Instruction::LoadConst {
10618 dst: reg(2),
10619 constant: cid(1),
10620 },
10621 Instruction::SetProperty {
10622 object: reg(0),
10623 key: reg(1),
10624 value: reg(2),
10625 },
10626 Instruction::GetProperty {
10627 dst: reg(3),
10628 object: reg(0),
10629 key: reg(1),
10630 },
10631 Instruction::Return { value: reg(3) },
10632 ],
10633 vec![],
10634 )],
10635 );
10636 let mut host = TestHost;
10637 let execution = Machine::new(&module, &mut host, Limits::default())
10638 .run()
10639 .expect("explicit undefined initializes the cell");
10640 assert_eq!(execution.value, Value::UNDEFINED);
10641 }
10642
10643 #[test]
10644 fn load_undeclared_global_throws_reference_error() {
10645 let module = verified(
10646 vec![Constant::String(EcmaString::from_utf8("missing"))],
10647 vec![function(
10648 0,
10649 2,
10650 vec![
10651 Instruction::LoadGlobal {
10652 dst: reg(0),
10653 name: cid(0),
10654 },
10655 Instruction::Halt,
10656 Instruction::Return { value: reg(1) },
10657 ],
10658 vec![ExceptionHandler {
10659 start: pc(0),
10660 end: pc(1),
10661 handler: pc(2),
10662 catch_register: reg(1),
10663 }],
10664 )],
10665 );
10666 let mut host = TestHost;
10667 let execution = Machine::new(&module, &mut host, Limits::default())
10670 .run()
10671 .unwrap();
10672 assert_eq!(execution.value, Value::UNDEFINED);
10673 }
10674
10675 #[test]
10676 fn uncaught_reference_error_reports_origin() {
10677 let module = verified(
10678 vec![Constant::String(EcmaString::from_utf8("missing"))],
10679 vec![function(
10680 0,
10681 1,
10682 vec![
10683 Instruction::LoadGlobal {
10684 dst: reg(0),
10685 name: cid(0),
10686 },
10687 Instruction::Return { value: reg(0) },
10688 ],
10689 vec![],
10690 )],
10691 );
10692 let mut host = TestHost;
10693 let error = Machine::new(&module, &mut host, Limits::default())
10694 .run()
10695 .unwrap_err();
10696 assert_eq!(error.pc, pc(0));
10697 assert!(matches!(
10698 error.kind,
10699 RuntimeErrorKind::UncaughtThrow {
10700 origin: ThrowOrigin::ReferenceError { .. },
10701 ..
10702 }
10703 ));
10704 }
10705
10706 fn assert_uri_error(global: &str, argument: EcmaString) {
10707 let module = verified(
10708 vec![
10709 Constant::String(EcmaString::from_utf8(global)),
10710 Constant::String(argument),
10711 Constant::Undefined,
10712 ],
10713 vec![function(
10714 0,
10715 5,
10716 vec![
10717 Instruction::LoadGlobal {
10718 dst: reg(0),
10719 name: cid(0),
10720 },
10721 Instruction::LoadConst {
10722 dst: reg(1),
10723 constant: cid(1),
10724 },
10725 Instruction::LoadConst {
10726 dst: reg(2),
10727 constant: cid(2),
10728 },
10729 Instruction::CreateArray { dst: reg(3) },
10730 Instruction::ArrayPush {
10731 array: reg(3),
10732 value: reg(1),
10733 },
10734 Instruction::Call {
10735 dst: reg(4),
10736 callee: reg(0),
10737 this_value: reg(2),
10738 arguments: reg(3),
10739 },
10740 Instruction::Return { value: reg(4) },
10741 ],
10742 Vec::new(),
10743 )],
10744 );
10745 let mut host = TestHost;
10746 let error = Machine::new(&module, &mut host, Limits::default())
10747 .run()
10748 .unwrap_err();
10749 assert_eq!(error.pc, pc(5));
10750 assert!(matches!(
10751 error.kind,
10752 RuntimeErrorKind::UncaughtThrow {
10753 origin: ThrowOrigin::UriError {
10754 operation: "URI malformed"
10755 },
10756 ..
10757 }
10758 ));
10759 }
10760
10761 #[test]
10762 fn uri_builtins_report_uri_error() {
10763 for (global, argument) in [
10764 ("encodeURIComponent", EcmaString::from_units(&[0xd800])),
10765 ("decodeURIComponent", EcmaString::from_utf8("%")),
10766 ("decodeURIComponent", EcmaString::from_utf8("%GG")),
10767 ("decodeURIComponent", EcmaString::from_utf8("%FF")),
10768 ("decodeURIComponent", EcmaString::from_utf8("%80")),
10769 ("decodeURIComponent", EcmaString::from_utf8("%C0%80")),
10770 ("decodeURIComponent", EcmaString::from_utf8("%E2%82")),
10771 ("decodeURIComponent", EcmaString::from_utf8("%ED%A0%80")),
10772 ("decodeURIComponent", EcmaString::from_utf8("%F4%90%80%80")),
10773 (
10774 "decodeURIComponent",
10775 EcmaString::from_utf8("%F8%80%80%80%80"),
10776 ),
10777 ] {
10778 assert_uri_error(global, argument);
10779 }
10780 }
10781
10782 fn assert_uri_decode(argument: EcmaString, expected: EcmaString) {
10783 let module = verified(
10784 vec![
10785 Constant::String(EcmaString::from_utf8("decodeURIComponent")),
10786 Constant::String(argument),
10787 Constant::Undefined,
10788 Constant::String(expected),
10789 ],
10790 vec![function(
10791 0,
10792 7,
10793 vec![
10794 Instruction::LoadGlobal {
10795 dst: reg(0),
10796 name: cid(0),
10797 },
10798 Instruction::LoadConst {
10799 dst: reg(1),
10800 constant: cid(1),
10801 },
10802 Instruction::LoadConst {
10803 dst: reg(2),
10804 constant: cid(2),
10805 },
10806 Instruction::CreateArray { dst: reg(3) },
10807 Instruction::ArrayPush {
10808 array: reg(3),
10809 value: reg(1),
10810 },
10811 Instruction::Call {
10812 dst: reg(4),
10813 callee: reg(0),
10814 this_value: reg(2),
10815 arguments: reg(3),
10816 },
10817 Instruction::LoadConst {
10818 dst: reg(5),
10819 constant: cid(3),
10820 },
10821 Instruction::Binary {
10822 dst: reg(6),
10823 op: BinaryOp::StrictEqual,
10824 left: reg(4),
10825 right: reg(5),
10826 },
10827 Instruction::Return { value: reg(6) },
10828 ],
10829 Vec::new(),
10830 )],
10831 );
10832 let mut host = TestHost;
10833 let execution = Machine::new(&module, &mut host, Limits::default())
10834 .run()
10835 .unwrap();
10836 assert_eq!(execution.value, Value::TRUE);
10837 }
10838
10839 #[test]
10840 fn decode_uri_component_preserves_units_and_decodes_utf8() {
10841 let exact = EcmaString::from_units(&[0xd800, 0x61, 0xdfff]);
10842 for (argument, expected) in [
10843 (exact.clone(), exact),
10844 (EcmaString::from_utf8("%2F"), EcmaString::from_utf8("/")),
10845 (
10846 EcmaString::from_utf8("%F0%9F%98%80"),
10847 EcmaString::from_utf8("😀"),
10848 ),
10849 (
10850 EcmaString::from_utf8("%E4%B8%ADA"),
10851 EcmaString::from_utf8("ä¸A"),
10852 ),
10853 (EcmaString::from_utf8("%00"), EcmaString::from_units(&[0])),
10854 ] {
10855 assert_uri_decode(argument, expected);
10856 }
10857 }
10858
10859 #[test]
10860 fn regexp_is_object_with_source_and_flags() {
10861 let module = verified(
10863 vec![
10864 Constant::String(EcmaString::from_utf8("ab")),
10865 Constant::String(EcmaString::from_utf8("gi")),
10866 Constant::String(EcmaString::from_utf8("source")),
10867 Constant::String(EcmaString::from_utf8("global")),
10868 ],
10869 vec![function(
10870 0,
10871 4,
10872 vec![
10873 Instruction::CreateRegExp {
10874 dst: reg(0),
10875 pattern: cid(0),
10876 flags: cid(1),
10877 },
10878 Instruction::LoadConst {
10879 dst: reg(1),
10880 constant: cid(3),
10881 },
10882 Instruction::GetProperty {
10883 dst: reg(2),
10884 object: reg(0),
10885 key: reg(1),
10886 },
10887 Instruction::Unary {
10888 dst: reg(3),
10889 op: UnaryOp::TypeOf,
10890 operand: reg(0),
10891 },
10892 Instruction::Return { value: reg(2) },
10893 ],
10894 vec![],
10895 )],
10896 );
10897 let execution = run_ok(&module);
10898 assert_eq!(execution.value, Value::TRUE);
10900 }
10901
10902 #[test]
10903 fn this_and_new_target_are_frame_owned() {
10904 let entry = function(
10906 0,
10907 4,
10908 vec![
10909 Instruction::CreateObject { dst: reg(0) },
10910 Instruction::CreateArray { dst: reg(3) },
10911 Instruction::CreateClosure {
10912 dst: reg(1),
10913 function: FunctionId::new(1),
10914 captures: reg(3),
10915 },
10916 Instruction::CreateArray { dst: reg(2) },
10917 Instruction::Call {
10918 dst: reg(0),
10919 callee: reg(1),
10920 this_value: reg(0),
10921 arguments: reg(2),
10922 },
10923 Instruction::Return { value: reg(0) },
10924 ],
10925 vec![],
10926 );
10927 let callee = function(
10930 0,
10931 2,
10932 vec![
10933 Instruction::LoadNewTarget { dst: reg(0) },
10934 Instruction::Unary {
10935 dst: reg(1),
10936 op: UnaryOp::TypeOf,
10937 operand: reg(0),
10938 },
10939 Instruction::Return { value: reg(1) },
10940 ],
10941 vec![],
10942 );
10943 let module = verified(vec![], vec![entry, callee]);
10944 let execution = run_ok(&module);
10945 assert_ne!(execution.value, Value::UNDEFINED);
10949 }
10950
10951 #[test]
10952 fn new_target_is_constructor_during_construct() {
10953 let entry = function(
10956 0,
10957 4,
10958 vec![
10959 Instruction::CreateArray { dst: reg(3) },
10960 Instruction::CreateClosure {
10961 dst: reg(0),
10962 function: FunctionId::new(1),
10963 captures: reg(3),
10964 },
10965 Instruction::CreateObject { dst: reg(1) },
10967 Instruction::LoadConst {
10968 dst: reg(2),
10969 constant: cid(0),
10970 },
10971 Instruction::SetProperty {
10972 object: reg(0),
10973 key: reg(2),
10974 value: reg(1),
10975 },
10976 Instruction::CreateArray { dst: reg(3) },
10977 Instruction::Construct {
10978 dst: reg(1),
10979 callee: reg(0),
10980 arguments: reg(3),
10981 },
10982 Instruction::LoadConst {
10984 dst: reg(2),
10985 constant: cid(1),
10986 },
10987 Instruction::GetProperty {
10988 dst: reg(3),
10989 object: reg(1),
10990 key: reg(2),
10991 },
10992 Instruction::Binary {
10993 dst: reg(3),
10994 op: BinaryOp::StrictEqual,
10995 left: reg(3),
10996 right: reg(0),
10997 },
10998 Instruction::Return { value: reg(3) },
10999 ],
11000 vec![],
11001 );
11002 let ctor = function(
11003 0,
11004 3,
11005 vec![
11006 Instruction::LoadNewTarget { dst: reg(0) },
11007 Instruction::LoadThis { dst: reg(1) },
11008 Instruction::LoadConst {
11009 dst: reg(2),
11010 constant: cid(1),
11011 },
11012 Instruction::SetProperty {
11013 object: reg(1),
11014 key: reg(2),
11015 value: reg(0),
11016 },
11017 Instruction::Halt,
11018 ],
11019 vec![],
11020 );
11021 let module = verified(
11022 vec![
11023 Constant::String(EcmaString::from_utf8("prototype")),
11024 Constant::String(EcmaString::from_utf8("nt")),
11025 ],
11026 vec![entry, ctor],
11027 );
11028 assert_eq!(run_ok(&module).value, Value::TRUE);
11029 }
11030
11031 #[test]
11032 fn arguments_object_reflects_passed_values() {
11033 let entry = function(
11035 0,
11036 4,
11037 vec![
11038 Instruction::CreateArray { dst: reg(3) },
11039 Instruction::CreateClosure {
11040 dst: reg(0),
11041 function: FunctionId::new(1),
11042 captures: reg(3),
11043 },
11044 Instruction::CreateArray { dst: reg(2) },
11046 Instruction::LoadConst {
11047 dst: reg(1),
11048 constant: cid(0),
11049 },
11050 Instruction::ArrayPush {
11051 array: reg(2),
11052 value: reg(1),
11053 },
11054 Instruction::Call {
11055 dst: reg(0),
11056 callee: reg(0),
11057 this_value: reg(1),
11058 arguments: reg(2),
11059 },
11060 Instruction::Return { value: reg(0) },
11061 ],
11062 vec![],
11063 );
11064 let callee = function(
11065 0,
11066 2,
11067 vec![
11068 Instruction::LoadArguments { dst: reg(0) },
11069 Instruction::LoadConst {
11070 dst: reg(1),
11071 constant: cid(1),
11072 },
11073 Instruction::GetProperty {
11074 dst: reg(0),
11075 object: reg(0),
11076 key: reg(1),
11077 },
11078 Instruction::Return { value: reg(0) },
11079 ],
11080 vec![],
11081 );
11082 let module = verified(
11083 vec![
11084 Constant::Int32(42),
11085 Constant::String(EcmaString::from_utf8("0")),
11086 ],
11087 vec![entry, callee],
11088 );
11089 assert_eq!(run_ok(&module).value, Value::int32(42));
11090 }
11091
11092 #[test]
11093 fn catch_register_receives_exact_thrown_value() {
11094 let module = verified(
11095 vec![Constant::Int32(9)],
11096 vec![function(
11097 0,
11098 2,
11099 vec![
11100 Instruction::LoadConst {
11101 dst: reg(0),
11102 constant: cid(0),
11103 },
11104 Instruction::Throw { value: reg(0) },
11105 Instruction::Return { value: reg(1) },
11106 ],
11107 vec![ExceptionHandler {
11108 start: pc(1),
11109 end: pc(2),
11110 handler: pc(2),
11111 catch_register: reg(1),
11112 }],
11113 )],
11114 );
11115 assert_eq!(run_ok(&module).value, Value::int32(9));
11116 }
11117
11118 #[test]
11119 fn native_callback_throw_is_caught_at_outer_call_site() {
11120 let entry = function(
11121 0,
11122 9,
11123 vec![
11124 Instruction::CreateArray { dst: reg(0) },
11125 Instruction::LoadConst {
11126 dst: reg(1),
11127 constant: cid(0),
11128 },
11129 Instruction::ArrayPush {
11130 array: reg(0),
11131 value: reg(1),
11132 },
11133 Instruction::CreateArray { dst: reg(2) },
11134 Instruction::CreateClosure {
11135 dst: reg(3),
11136 function: FunctionId::new(1),
11137 captures: reg(2),
11138 },
11139 Instruction::LoadConst {
11140 dst: reg(4),
11141 constant: cid(1),
11142 },
11143 Instruction::GetProperty {
11144 dst: reg(5),
11145 object: reg(0),
11146 key: reg(4),
11147 },
11148 Instruction::CreateArray { dst: reg(6) },
11149 Instruction::ArrayPush {
11150 array: reg(6),
11151 value: reg(3),
11152 },
11153 Instruction::Call {
11154 dst: reg(7),
11155 callee: reg(5),
11156 this_value: reg(0),
11157 arguments: reg(6),
11158 },
11159 Instruction::Halt,
11160 Instruction::Return { value: reg(8) },
11161 ],
11162 vec![ExceptionHandler {
11163 start: pc(9),
11164 end: pc(10),
11165 handler: pc(11),
11166 catch_register: reg(8),
11167 }],
11168 );
11169 let callback = closure_function(
11170 0,
11171 0,
11172 1,
11173 vec![
11174 Instruction::LoadConst {
11175 dst: reg(0),
11176 constant: cid(0),
11177 },
11178 Instruction::Throw { value: reg(0) },
11179 ],
11180 );
11181 let module = verified(
11182 vec![
11183 Constant::Int32(7),
11184 Constant::String(EcmaString::from_utf8("map")),
11185 ],
11186 vec![entry, callback],
11187 );
11188
11189 assert_eq!(run_ok(&module).value, Value::int32(7));
11190 }
11191
11192 #[test]
11193 fn native_callback_throw_uncaught_at_outer_call_site() {
11194 let entry = function(
11195 0,
11196 9,
11197 vec![
11198 Instruction::CreateArray { dst: reg(0) },
11199 Instruction::LoadConst {
11200 dst: reg(1),
11201 constant: cid(0),
11202 },
11203 Instruction::ArrayPush {
11204 array: reg(0),
11205 value: reg(1),
11206 },
11207 Instruction::CreateArray { dst: reg(2) },
11208 Instruction::CreateClosure {
11209 dst: reg(3),
11210 function: FunctionId::new(1),
11211 captures: reg(2),
11212 },
11213 Instruction::LoadConst {
11214 dst: reg(4),
11215 constant: cid(1),
11216 },
11217 Instruction::GetProperty {
11218 dst: reg(5),
11219 object: reg(0),
11220 key: reg(4),
11221 },
11222 Instruction::CreateArray { dst: reg(6) },
11223 Instruction::ArrayPush {
11224 array: reg(6),
11225 value: reg(3),
11226 },
11227 Instruction::Call {
11228 dst: reg(7),
11229 callee: reg(5),
11230 this_value: reg(0),
11231 arguments: reg(6),
11232 },
11233 Instruction::Halt,
11234 ],
11235 Vec::new(),
11236 );
11237 let callback = closure_function(
11238 0,
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 );
11249 let simple = closure_function(
11250 0,
11251 0,
11252 1,
11253 vec![
11254 Instruction::LoadConst {
11255 dst: reg(0),
11256 constant: cid(2),
11257 },
11258 Instruction::Return { value: reg(0) },
11259 ],
11260 );
11261 let module = verified(
11262 vec![
11263 Constant::Int32(7),
11264 Constant::String(EcmaString::from_utf8("map")),
11265 Constant::Int32(42),
11266 ],
11267 vec![entry, callback, simple],
11268 );
11269
11270 let mut host = TestHost;
11271 let mut machine = Machine::new(&module, &mut host, Limits::default());
11272 let error = machine.run_loop(0).unwrap_err();
11273 assert_eq!(
11274 error.kind,
11275 RuntimeErrorKind::UncaughtThrow {
11276 value: Value::int32(7),
11277 origin: ThrowOrigin::Bytecode,
11278 }
11279 );
11280 assert!(machine.callback_boundaries.is_empty());
11281 assert!(machine.frames.is_empty());
11282 assert_eq!(machine.live_registers, 0);
11283
11284 let callee = machine
11285 .allocate(HeapEntry::Function {
11286 module: ModuleId::new(0),
11287 function: FunctionId::new(2),
11288 captures: Vec::new(),
11289 properties: PropertyMap::default(),
11290 prototype: Some(machine.intrinsics.function_prototype),
11291 extensible: true,
11292 })
11293 .unwrap();
11294 assert_eq!(
11295 machine.call_value(callee, Value::UNDEFINED, &[]).unwrap(),
11296 Value::int32(42)
11297 );
11298 }
11299
11300 #[test]
11301 fn callee_throw_unwinds_to_call_site_handler() {
11302 let entry = function(
11303 0,
11304 4,
11305 vec![
11306 Instruction::CreateArray { dst: reg(3) },
11307 Instruction::CreateClosure {
11308 dst: reg(0),
11309 function: FunctionId::new(1),
11310 captures: reg(3),
11311 },
11312 Instruction::CreateArray { dst: reg(1) },
11313 Instruction::Call {
11314 dst: reg(2),
11315 callee: reg(0),
11316 this_value: reg(1),
11317 arguments: reg(1),
11318 },
11319 Instruction::Halt,
11320 Instruction::Return { value: reg(3) },
11321 ],
11322 vec![ExceptionHandler {
11323 start: pc(3),
11324 end: pc(4),
11325 handler: pc(5),
11326 catch_register: reg(3),
11327 }],
11328 );
11329 let callee = function(
11330 0,
11331 1,
11332 vec![
11333 Instruction::LoadConst {
11334 dst: reg(0),
11335 constant: cid(0),
11336 },
11337 Instruction::Throw { value: reg(0) },
11338 ],
11339 vec![],
11340 );
11341 let module = verified(vec![Constant::Int32(7)], vec![entry, callee]);
11342 assert_eq!(run_ok(&module).value, Value::int32(7));
11343 }
11344
11345 #[test]
11346 fn heap_and_register_limits_fail_before_unbounded_growth() {
11347 let module = verified(
11348 vec![],
11349 vec![function(
11350 0,
11351 2,
11352 vec![
11353 Instruction::CreateObject { dst: reg(0) },
11354 Instruction::CreateObject { dst: reg(1) },
11355 Instruction::Halt,
11356 ],
11357 vec![],
11358 )],
11359 );
11360 let mut host = TestHost;
11361 let error = Machine::new(
11362 &module,
11363 &mut host,
11364 Limits {
11365 max_heap_slots: 1,
11366 ..Limits::default()
11367 },
11368 )
11369 .run()
11370 .unwrap_err();
11371 assert_eq!(error.pc, pc(1));
11372 assert_eq!(
11373 error.kind,
11374 RuntimeErrorKind::HeapSlotLimitExceeded { limit: 1 }
11375 );
11376
11377 let mut host = TestHost;
11378 let error = Machine::new(
11379 &module,
11380 &mut host,
11381 Limits {
11382 max_total_registers: 1,
11383 ..Limits::default()
11384 },
11385 )
11386 .run()
11387 .unwrap_err();
11388 assert_eq!(
11389 error.kind,
11390 RuntimeErrorKind::RegisterLimitExceeded { limit: 1 }
11391 );
11392 }
11393
11394 #[test]
11395 fn argument_array_length_limit_is_enforced() {
11396 let entry = function(
11397 0,
11398 4,
11399 vec![
11400 Instruction::CreateArray { dst: reg(3) },
11401 Instruction::CreateClosure {
11402 dst: reg(0),
11403 function: FunctionId::new(1),
11404 captures: reg(3),
11405 },
11406 Instruction::CreateArray { dst: reg(2) },
11407 Instruction::LoadConst {
11408 dst: reg(1),
11409 constant: cid(0),
11410 },
11411 Instruction::ArrayPush {
11412 array: reg(2),
11413 value: reg(1),
11414 },
11415 Instruction::Call {
11416 dst: reg(0),
11417 callee: reg(0),
11418 this_value: reg(1),
11419 arguments: reg(2),
11420 },
11421 Instruction::Halt,
11422 ],
11423 vec![],
11424 );
11425 let callee = function(1, 1, vec![Instruction::Return { value: reg(0) }], vec![]);
11426 let module = verified(vec![Constant::Int32(1)], vec![entry, callee]);
11427 let mut host = TestHost;
11428 let error = Machine::new(
11429 &module,
11430 &mut host,
11431 Limits {
11432 max_argument_count: 0,
11433 ..Limits::default()
11434 },
11435 )
11436 .run()
11437 .unwrap_err();
11438 assert_eq!(
11439 error.kind,
11440 RuntimeErrorKind::ArgumentLimitExceeded {
11441 limit: 0,
11442 requested: 1
11443 }
11444 );
11445 }
11446
11447 #[test]
11448 fn u32_registers_and_instruction_pcs_do_not_truncate_at_127() {
11449 let mut code = vec![Instruction::LoadConst {
11450 dst: reg(0),
11451 constant: cid(0),
11452 }];
11453 for register in 1..=199 {
11454 code.push(Instruction::Move {
11455 dst: reg(register),
11456 src: reg(register - 1),
11457 });
11458 }
11459 code.push(Instruction::Return { value: reg(199) });
11460 let module = verified(
11461 vec![Constant::Number(NumberBits::from_f64(3.5))],
11462 vec![function(0, 200, code, vec![])],
11463 );
11464 let execution = run_ok(&module);
11465 assert_eq!(execution.value, Value::number(3.5));
11466 assert_eq!(execution.entry_registers[199], Value::number(3.5));
11467 }
11468
11469 #[test]
11470 fn construct_returned_object_overrides_default_instance() {
11471 let entry = function(
11473 0,
11474 3,
11475 vec![
11476 Instruction::CreateArray { dst: reg(2) },
11477 Instruction::CreateClosure {
11478 dst: reg(0),
11479 function: FunctionId::new(1),
11480 captures: reg(2),
11481 },
11482 Instruction::CreateArray { dst: reg(2) },
11483 Instruction::Construct {
11484 dst: reg(1),
11485 callee: reg(0),
11486 arguments: reg(2),
11487 },
11488 Instruction::LoadConst {
11490 dst: reg(0),
11491 constant: cid(0),
11492 },
11493 Instruction::GetProperty {
11494 dst: reg(2),
11495 object: reg(1),
11496 key: reg(0),
11497 },
11498 Instruction::Return { value: reg(2) },
11499 ],
11500 vec![],
11501 );
11502 let returns_object = function(
11503 0,
11504 3,
11505 vec![
11506 Instruction::CreateObject { dst: reg(0) },
11507 Instruction::LoadConst {
11508 dst: reg(1),
11509 constant: cid(0),
11510 },
11511 Instruction::LoadConst {
11512 dst: reg(2),
11513 constant: cid(1),
11514 },
11515 Instruction::SetProperty {
11516 object: reg(0),
11517 key: reg(1),
11518 value: reg(2),
11519 },
11520 Instruction::Return { value: reg(0) },
11521 ],
11522 vec![],
11523 );
11524 let module = verified(
11525 vec![
11526 Constant::String(EcmaString::from_utf8("marker")),
11527 Constant::Int32(5),
11528 ],
11529 vec![entry, returns_object],
11530 );
11531 assert_eq!(run_ok(&module).value, Value::int32(5));
11532 }
11533
11534 #[test]
11535 fn ecmascript_number_formatting_is_shortest_round_trip() {
11536 let cases = [
11537 (0.1 + 0.2, "0.30000000000000004"),
11538 (1e21, "1e+21"),
11539 (-0.0, "0"),
11540 (1.0 / 3.0, "0.3333333333333333"),
11541 (1e-6, "0.000001"),
11542 (1e-7, "1e-7"),
11543 ];
11544 for (number, expected) in cases {
11545 assert_eq!(
11546 Machine::<TestHost>::ordinary_number_to_string(number),
11547 expected
11548 );
11549 }
11550 }
11551
11552 #[test]
11553 fn own_keys_put_indices_before_insertion_ordered_strings() {
11554 let module = verified(
11555 Vec::new(),
11556 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11557 );
11558 let mut host = TestHost;
11559 let mut machine = Machine::new(&module, &mut host, Limits::default());
11560 let object = machine
11561 .allocate(HeapEntry::Object {
11562 properties: PropertyMap::default(),
11563 prototype: Some(machine.intrinsics.object_prototype),
11564 boxed_primitive: None,
11565 extensible: true,
11566 })
11567 .unwrap();
11568 let index = machine.runtime_slot(object).unwrap().unwrap();
11569 for (key, value) in [("b", 1), ("2", 2), ("a", 3), ("1", 4)] {
11570 machine
11571 .set_own_data(
11572 index,
11573 PropertyKey::Named(EcmaString::from_utf8(key)),
11574 Value::int32(value),
11575 )
11576 .unwrap();
11577 }
11578 assert_eq!(
11579 machine.enumerable_keys(object).unwrap(),
11580 ["1", "2", "b", "a"].map(EcmaString::from_utf8)
11581 );
11582 }
11583
11584 #[test]
11585 fn object_prototype_to_string_uses_realm_tags() {
11586 let module = verified(
11587 Vec::new(),
11588 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11589 );
11590 let mut host = TestHost;
11591 let mut machine = Machine::new(&module, &mut host, Limits::default());
11592 let array = machine
11593 .allocate(HeapEntry::Array {
11594 elements: Vec::new(),
11595 properties: PropertyMap::default(),
11596 prototype: Some(machine.intrinsics.array_prototype),
11597 extensible: true,
11598 length_writable: true,
11599 })
11600 .unwrap();
11601 let object = machine
11602 .allocate(HeapEntry::Object {
11603 properties: PropertyMap::default(),
11604 prototype: Some(machine.intrinsics.object_prototype),
11605 boxed_primitive: None,
11606 extensible: true,
11607 })
11608 .unwrap();
11609 let function = machine.intrinsics.global("Object").unwrap();
11610 let to_string = machine.intrinsics.object_to_string();
11611 for (value, expected) in [
11612 (Value::UNDEFINED, "[object Undefined]"),
11613 (Value::NULL, "[object Null]"),
11614 (Value::TRUE, "[object Boolean]"),
11615 (array, "[object Array]"),
11616 (object, "[object Object]"),
11617 (function, "[object Function]"),
11618 ] {
11619 let tag = machine.call_value(to_string, value, &[]).unwrap();
11620 assert!(
11621 machine
11622 .string_text(tag)
11623 .is_some_and(|text| text.eq_ascii(expected))
11624 );
11625 }
11626 }
11627
11628 #[derive(Default)]
11629 struct CapabilityHost {
11630 stdout: Vec<u8>,
11631 stderr: Vec<u8>,
11632 env: BTreeMap<String, String>,
11633 }
11634
11635 impl Host for CapabilityHost {
11636 fn write_stdout(&mut self, bytes: &[u8]) {
11637 self.stdout.extend_from_slice(bytes);
11638 }
11639
11640 fn write_stderr(&mut self, bytes: &[u8]) {
11641 self.stderr.extend_from_slice(bytes);
11642 }
11643
11644 fn env(&self, name: &str) -> Option<&str> {
11645 self.env.get(name).map(String::as_str)
11646 }
11647
11648 fn set_env(&mut self, name: &str, value: &str) {
11649 self.env.insert(name.to_owned(), value.to_owned());
11650 }
11651
11652 fn delete_env(&mut self, name: &str) -> bool {
11653 self.env.remove(name).is_some()
11654 }
11655 }
11656
11657 #[test]
11658 fn console_formats_node_value_shapes_byte_exactly() {
11659 let module = verified(
11660 Vec::new(),
11661 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11662 );
11663 let mut host = CapabilityHost::default();
11664 {
11665 let mut machine = Machine::new(&module, &mut host, Limits::default());
11666 let console = machine.intrinsics.global("console").unwrap();
11667 let log = machine.get_named_property(console, "log").unwrap();
11668 let string = machine
11669 .allocate(HeapEntry::String(EcmaString::from_utf8("hello")))
11670 .unwrap();
11671 let array_string = machine
11672 .allocate(HeapEntry::String(EcmaString::from_utf8("x")))
11673 .unwrap();
11674 let array = machine
11675 .allocate(HeapEntry::Array {
11676 elements: vec![Value::int32(1), array_string],
11677 properties: PropertyMap::default(),
11678 prototype: Some(machine.intrinsics.array_prototype),
11679 extensible: true,
11680 length_writable: true,
11681 })
11682 .unwrap();
11683 let mut inner_properties = PropertyMap::default();
11684 inner_properties.insert(
11685 PropertyKey::Named(EcmaString::from_utf8("answer")),
11686 Property::Data {
11687 value: Value::int32(42),
11688 writable: true,
11689 enumerable: true,
11690 configurable: true,
11691 },
11692 );
11693 let inner = machine
11694 .allocate(HeapEntry::Object {
11695 properties: inner_properties,
11696 prototype: Some(machine.intrinsics.object_prototype),
11697 boxed_primitive: None,
11698 extensible: true,
11699 })
11700 .unwrap();
11701 let mut outer_properties = PropertyMap::default();
11702 outer_properties.insert(
11703 PropertyKey::Named(EcmaString::from_utf8("nested")),
11704 Property::Data {
11705 value: inner,
11706 writable: true,
11707 enumerable: true,
11708 configurable: true,
11709 },
11710 );
11711 let outer = machine
11712 .allocate(HeapEntry::Object {
11713 properties: outer_properties,
11714 prototype: Some(machine.intrinsics.object_prototype),
11715 boxed_primitive: None,
11716 extensible: true,
11717 })
11718 .unwrap();
11719 let symbol = machine
11720 .allocate(HeapEntry::Symbol {
11721 description: EcmaString::from_utf8("token"),
11722 })
11723 .unwrap();
11724 for value in [
11725 string,
11726 Value::int32(42),
11727 array,
11728 outer,
11729 Value::UNDEFINED,
11730 Value::NULL,
11731 symbol,
11732 ] {
11733 machine.call_value(log, console, &[value]).unwrap();
11734 }
11735 }
11736 assert_eq!(
11737 host.stdout,
11738 b"hello\n42\n[ 1, 'x' ]\n{ nested: { answer: 42 } }\nundefined\nnull\nSymbol(token)\n"
11739 );
11740 assert!(host.stderr.is_empty());
11741 }
11742
11743 #[test]
11744 fn console_and_process_properties_are_reassignable_and_env_is_live() {
11745 let module = verified(
11746 Vec::new(),
11747 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11748 );
11749 let mut host = CapabilityHost::default();
11750 {
11751 let mut machine = Machine::new(&module, &mut host, Limits::default());
11752 let console = machine.intrinsics.global("console").unwrap();
11753 let warn = machine.get_named_property(console, "warn").unwrap();
11754 machine
11755 .set_data_property(console, "warn", Value::int32(91))
11756 .unwrap();
11757 assert_eq!(
11758 machine.get_named_property(console, "warn").unwrap(),
11759 Value::int32(91)
11760 );
11761 machine.set_data_property(console, "warn", warn).unwrap();
11762
11763 let process = machine.intrinsics.global("process").unwrap();
11764 let env = machine.get_named_property(process, "env").unwrap();
11765 machine
11766 .set_data_property(env, "BAMTS_MODE", Value::int32(7))
11767 .unwrap();
11768 let value = machine.get_named_property(env, "BAMTS_MODE").unwrap();
11769 assert!(
11770 machine
11771 .string_text(value)
11772 .is_some_and(|text| text.eq_ascii("7"))
11773 );
11774 assert!(
11775 machine
11776 .delete_property(
11777 env,
11778 &PropertyKey::Named(EcmaString::from_utf8("BAMTS_MODE"))
11779 )
11780 .unwrap()
11781 );
11782 assert_eq!(
11783 machine.get_named_property(env, "BAMTS_MODE").unwrap(),
11784 Value::UNDEFINED
11785 );
11786 }
11787 assert_eq!(host.env("BAMTS_MODE"), None);
11788 }
11789
11790 #[test]
11791 fn independent_modules_keep_same_name_globals_isolated() {
11792 let dependency = |name: &str, value: i32| {
11793 program_module(
11794 name,
11795 vec![
11796 Constant::String(EcmaString::from_utf8("x")),
11797 Constant::Int32(value),
11798 ],
11799 vec![function(
11800 0,
11801 1,
11802 vec![
11803 Instruction::LoadConst {
11804 dst: reg(0),
11805 constant: cid(2),
11806 },
11807 Instruction::StoreGlobal {
11808 name: cid(1),
11809 value: reg(0),
11810 },
11811 Instruction::Return { value: reg(0) },
11812 ],
11813 Vec::new(),
11814 )],
11815 Vec::new(),
11816 vec![Binding {
11817 name: cid(1),
11818 kind: BindingKind::Hoisted,
11819 }],
11820 vec![Export {
11821 name: cid(1),
11822 source: ExportSource::Local(BindingId::new(0)),
11823 }],
11824 )
11825 };
11826 let root = program_module(
11827 "root",
11828 vec![
11829 Constant::String(EcmaString::from_utf8("left")),
11830 Constant::String(EcmaString::from_utf8("right")),
11831 Constant::String(EcmaString::from_utf8("x")),
11832 ],
11833 vec![function(
11834 0,
11835 5,
11836 vec![
11837 Instruction::LoadGlobal {
11838 dst: reg(0),
11839 name: cid(1),
11840 },
11841 Instruction::LoadGlobal {
11842 dst: reg(1),
11843 name: cid(2),
11844 },
11845 Instruction::LoadConst {
11846 dst: reg(2),
11847 constant: cid(3),
11848 },
11849 Instruction::GetProperty {
11850 dst: reg(3),
11851 object: reg(0),
11852 key: reg(2),
11853 },
11854 Instruction::GetProperty {
11855 dst: reg(4),
11856 object: reg(1),
11857 key: reg(2),
11858 },
11859 Instruction::Binary {
11860 dst: reg(0),
11861 op: BinaryOp::Add,
11862 left: reg(3),
11863 right: reg(4),
11864 },
11865 Instruction::Return { value: reg(0) },
11866 ],
11867 Vec::new(),
11868 )],
11869 vec![
11870 Edge {
11871 specifier: cid(1),
11872 target: EdgeTarget::Local(ModuleId::new(0)),
11873 kind: EdgeKind::Static,
11874 },
11875 Edge {
11876 specifier: cid(2),
11877 target: EdgeTarget::Local(ModuleId::new(1)),
11878 kind: EdgeKind::Static,
11879 },
11880 ],
11881 vec![
11882 Binding {
11883 name: cid(1),
11884 kind: BindingKind::Namespace {
11885 edge: EdgeId::new(0),
11886 },
11887 },
11888 Binding {
11889 name: cid(2),
11890 kind: BindingKind::Namespace {
11891 edge: EdgeId::new(1),
11892 },
11893 },
11894 ],
11895 Vec::new(),
11896 );
11897 let program = linked(vec![dependency("left", 1), dependency("right", 2), root], 2);
11898 assert_eq!(run_ok(&program).value, Value::int32(3));
11899 }
11900
11901 #[test]
11902 fn imported_binding_observes_post_link_mutation_live() {
11903 let dependency = program_module(
11904 "dependency",
11905 vec![
11906 Constant::String(EcmaString::from_utf8("x")),
11907 Constant::Int32(1),
11908 Constant::Int32(2),
11909 Constant::String(EcmaString::from_utf8("set")),
11910 ],
11911 vec![
11912 function(
11913 0,
11914 3,
11915 vec![
11916 Instruction::LoadConst {
11917 dst: reg(0),
11918 constant: cid(2),
11919 },
11920 Instruction::StoreGlobal {
11921 name: cid(1),
11922 value: reg(0),
11923 },
11924 Instruction::CreateArray { dst: reg(1) },
11925 Instruction::CreateClosure {
11926 dst: reg(2),
11927 function: FunctionId::new(1),
11928 captures: reg(1),
11929 },
11930 Instruction::StoreGlobal {
11931 name: cid(4),
11932 value: reg(2),
11933 },
11934 Instruction::Return { value: reg(0) },
11935 ],
11936 Vec::new(),
11937 ),
11938 function(
11939 0,
11940 1,
11941 vec![
11942 Instruction::LoadConst {
11943 dst: reg(0),
11944 constant: cid(3),
11945 },
11946 Instruction::StoreGlobal {
11947 name: cid(1),
11948 value: reg(0),
11949 },
11950 Instruction::Return { value: reg(0) },
11951 ],
11952 Vec::new(),
11953 ),
11954 ],
11955 Vec::new(),
11956 vec![
11957 Binding {
11958 name: cid(1),
11959 kind: BindingKind::Hoisted,
11960 },
11961 Binding {
11962 name: cid(4),
11963 kind: BindingKind::Hoisted,
11964 },
11965 ],
11966 vec![
11967 Export {
11968 name: cid(1),
11969 source: ExportSource::Local(BindingId::new(0)),
11970 },
11971 Export {
11972 name: cid(4),
11973 source: ExportSource::Local(BindingId::new(1)),
11974 },
11975 ],
11976 );
11977 let root = program_module(
11978 "root",
11979 vec![
11980 Constant::String(EcmaString::from_utf8("x")),
11981 Constant::String(EcmaString::from_utf8("set")),
11982 Constant::String(EcmaString::from_utf8("dep")),
11983 ],
11984 vec![function(
11985 0,
11986 3,
11987 vec![
11988 Instruction::LoadGlobal {
11989 dst: reg(0),
11990 name: cid(2),
11991 },
11992 Instruction::CreateArray { dst: reg(1) },
11993 Instruction::Call {
11994 dst: reg(2),
11995 callee: reg(0),
11996 this_value: reg(1),
11997 arguments: reg(1),
11998 },
11999 Instruction::LoadGlobal {
12000 dst: reg(0),
12001 name: cid(1),
12002 },
12003 Instruction::Return { value: reg(0) },
12004 ],
12005 Vec::new(),
12006 )],
12007 vec![Edge {
12008 specifier: cid(3),
12009 target: EdgeTarget::Local(ModuleId::new(0)),
12010 kind: EdgeKind::Static,
12011 }],
12012 vec![
12013 Binding {
12014 name: cid(1),
12015 kind: BindingKind::Imported {
12016 edge: EdgeId::new(0),
12017 name: cid(1),
12018 },
12019 },
12020 Binding {
12021 name: cid(2),
12022 kind: BindingKind::Imported {
12023 edge: EdgeId::new(0),
12024 name: cid(2),
12025 },
12026 },
12027 ],
12028 Vec::new(),
12029 );
12030 assert_eq!(
12031 run_ok(&linked(vec![dependency, root], 1)).value,
12032 Value::int32(2)
12033 );
12034 }
12035
12036 #[test]
12037 fn closure_globals_resolve_in_the_defining_module() {
12038 let dependency = program_module(
12039 "dependency",
12040 vec![
12041 Constant::String(EcmaString::from_utf8("x")),
12042 Constant::Int32(10),
12043 Constant::String(EcmaString::from_utf8("read")),
12044 ],
12045 vec![
12046 function(
12047 0,
12048 3,
12049 vec![
12050 Instruction::LoadConst {
12051 dst: reg(0),
12052 constant: cid(2),
12053 },
12054 Instruction::StoreGlobal {
12055 name: cid(1),
12056 value: reg(0),
12057 },
12058 Instruction::CreateArray { dst: reg(1) },
12059 Instruction::CreateClosure {
12060 dst: reg(2),
12061 function: FunctionId::new(1),
12062 captures: reg(1),
12063 },
12064 Instruction::StoreGlobal {
12065 name: cid(3),
12066 value: reg(2),
12067 },
12068 Instruction::Return { value: reg(0) },
12069 ],
12070 Vec::new(),
12071 ),
12072 function(
12073 0,
12074 1,
12075 vec![
12076 Instruction::LoadGlobal {
12077 dst: reg(0),
12078 name: cid(1),
12079 },
12080 Instruction::Return { value: reg(0) },
12081 ],
12082 Vec::new(),
12083 ),
12084 ],
12085 Vec::new(),
12086 vec![
12087 Binding {
12088 name: cid(1),
12089 kind: BindingKind::Hoisted,
12090 },
12091 Binding {
12092 name: cid(3),
12093 kind: BindingKind::Hoisted,
12094 },
12095 ],
12096 vec![Export {
12097 name: cid(3),
12098 source: ExportSource::Local(BindingId::new(1)),
12099 }],
12100 );
12101 let root = program_module(
12102 "root",
12103 vec![
12104 Constant::String(EcmaString::from_utf8("x")),
12105 Constant::Int32(20),
12106 Constant::String(EcmaString::from_utf8("read")),
12107 Constant::String(EcmaString::from_utf8("dep")),
12108 ],
12109 vec![function(
12110 0,
12111 4,
12112 vec![
12113 Instruction::LoadConst {
12114 dst: reg(0),
12115 constant: cid(2),
12116 },
12117 Instruction::StoreGlobal {
12118 name: cid(1),
12119 value: reg(0),
12120 },
12121 Instruction::LoadGlobal {
12122 dst: reg(1),
12123 name: cid(3),
12124 },
12125 Instruction::CreateArray { dst: reg(2) },
12126 Instruction::Call {
12127 dst: reg(3),
12128 callee: reg(1),
12129 this_value: reg(2),
12130 arguments: reg(2),
12131 },
12132 Instruction::Return { value: reg(3) },
12133 ],
12134 Vec::new(),
12135 )],
12136 vec![Edge {
12137 specifier: cid(4),
12138 target: EdgeTarget::Local(ModuleId::new(0)),
12139 kind: EdgeKind::Static,
12140 }],
12141 vec![
12142 Binding {
12143 name: cid(1),
12144 kind: BindingKind::Hoisted,
12145 },
12146 Binding {
12147 name: cid(3),
12148 kind: BindingKind::Imported {
12149 edge: EdgeId::new(0),
12150 name: cid(3),
12151 },
12152 },
12153 ],
12154 Vec::new(),
12155 );
12156 assert_eq!(
12157 run_ok(&linked(vec![dependency, root], 1)).value,
12158 Value::int32(10)
12159 );
12160 }
12161
12162 #[test]
12163 fn cycle_traps_a_lexical_read_before_initialization() {
12164 let first = program_module(
12165 "first",
12166 vec![
12167 Constant::String(EcmaString::from_utf8("a")),
12168 Constant::Int32(1),
12169 Constant::String(EcmaString::from_utf8("second")),
12170 ],
12171 vec![function(
12172 0,
12173 1,
12174 vec![
12175 Instruction::LoadConst {
12176 dst: reg(0),
12177 constant: cid(2),
12178 },
12179 Instruction::StoreGlobal {
12180 name: cid(1),
12181 value: reg(0),
12182 },
12183 Instruction::Return { value: reg(0) },
12184 ],
12185 Vec::new(),
12186 )],
12187 vec![Edge {
12188 specifier: cid(3),
12189 target: EdgeTarget::Local(ModuleId::new(1)),
12190 kind: EdgeKind::Static,
12191 }],
12192 vec![Binding {
12193 name: cid(1),
12194 kind: BindingKind::Lexical,
12195 }],
12196 vec![Export {
12197 name: cid(1),
12198 source: ExportSource::Local(BindingId::new(0)),
12199 }],
12200 );
12201 let second = program_module(
12202 "second",
12203 vec![
12204 Constant::String(EcmaString::from_utf8("a")),
12205 Constant::String(EcmaString::from_utf8("first")),
12206 ],
12207 vec![function(
12208 0,
12209 1,
12210 vec![
12211 Instruction::LoadGlobal {
12212 dst: reg(0),
12213 name: cid(1),
12214 },
12215 Instruction::Return { value: reg(0) },
12216 ],
12217 Vec::new(),
12218 )],
12219 vec![Edge {
12220 specifier: cid(2),
12221 target: EdgeTarget::Local(ModuleId::new(0)),
12222 kind: EdgeKind::Static,
12223 }],
12224 vec![Binding {
12225 name: cid(1),
12226 kind: BindingKind::Imported {
12227 edge: EdgeId::new(0),
12228 name: cid(1),
12229 },
12230 }],
12231 Vec::new(),
12232 );
12233 let program = linked(vec![first, second], 0);
12234 let mut host = TestHost;
12235 let error = Machine::new(&program, &mut host, Limits::default())
12236 .run()
12237 .unwrap_err();
12238 assert!(matches!(
12239 error.kind,
12240 RuntimeErrorKind::TemporalDeadZone { module, binding }
12241 if module == ModuleId::new(1) && binding == BindingId::new(0)
12242 ));
12243 }
12244
12245 #[test]
12246 fn cycle_reentry_with_a_hoisted_binding_completes() {
12247 let first = program_module(
12248 "first",
12249 vec![
12250 Constant::String(EcmaString::from_utf8("a")),
12251 Constant::Int32(1),
12252 Constant::String(EcmaString::from_utf8("second")),
12253 ],
12254 vec![function(
12255 0,
12256 1,
12257 vec![
12258 Instruction::LoadConst {
12259 dst: reg(0),
12260 constant: cid(2),
12261 },
12262 Instruction::StoreGlobal {
12263 name: cid(1),
12264 value: reg(0),
12265 },
12266 Instruction::Return { value: reg(0) },
12267 ],
12268 Vec::new(),
12269 )],
12270 vec![Edge {
12271 specifier: cid(3),
12272 target: EdgeTarget::Local(ModuleId::new(1)),
12273 kind: EdgeKind::Static,
12274 }],
12275 vec![Binding {
12276 name: cid(1),
12277 kind: BindingKind::Hoisted,
12278 }],
12279 vec![Export {
12280 name: cid(1),
12281 source: ExportSource::Local(BindingId::new(0)),
12282 }],
12283 );
12284 let second = program_module(
12285 "second",
12286 vec![
12287 Constant::String(EcmaString::from_utf8("a")),
12288 Constant::String(EcmaString::from_utf8("first")),
12289 ],
12290 vec![function(
12291 0,
12292 1,
12293 vec![
12294 Instruction::LoadGlobal {
12295 dst: reg(0),
12296 name: cid(1),
12297 },
12298 Instruction::Return { value: reg(0) },
12299 ],
12300 Vec::new(),
12301 )],
12302 vec![Edge {
12303 specifier: cid(2),
12304 target: EdgeTarget::Local(ModuleId::new(0)),
12305 kind: EdgeKind::Static,
12306 }],
12307 vec![Binding {
12308 name: cid(1),
12309 kind: BindingKind::Imported {
12310 edge: EdgeId::new(0),
12311 name: cid(1),
12312 },
12313 }],
12314 Vec::new(),
12315 );
12316 assert_eq!(
12317 run_ok(&linked(vec![first, second], 0)).value,
12318 Value::int32(1)
12319 );
12320 }
12321
12322 #[test]
12323 fn namespace_identity_reads_live_cells_and_enumerates_sorted_keys() {
12324 let dependency = program_module(
12325 "dependency",
12326 vec![
12327 Constant::String(EcmaString::from_utf8("z")),
12328 Constant::String(EcmaString::from_utf8("a")),
12329 Constant::String(EcmaString::from_utf8("mutate")),
12330 Constant::Int32(1),
12331 Constant::Int32(2),
12332 Constant::Int32(3),
12333 ],
12334 vec![
12335 function(
12336 0,
12337 4,
12338 vec![
12339 Instruction::LoadConst {
12340 dst: reg(0),
12341 constant: cid(4),
12342 },
12343 Instruction::StoreGlobal {
12344 name: cid(1),
12345 value: reg(0),
12346 },
12347 Instruction::LoadConst {
12348 dst: reg(0),
12349 constant: cid(5),
12350 },
12351 Instruction::StoreGlobal {
12352 name: cid(2),
12353 value: reg(0),
12354 },
12355 Instruction::CreateArray { dst: reg(1) },
12356 Instruction::CreateClosure {
12357 dst: reg(2),
12358 function: FunctionId::new(1),
12359 captures: reg(1),
12360 },
12361 Instruction::StoreGlobal {
12362 name: cid(3),
12363 value: reg(2),
12364 },
12365 Instruction::Return { value: reg(0) },
12366 ],
12367 Vec::new(),
12368 ),
12369 function(
12370 0,
12371 1,
12372 vec![
12373 Instruction::LoadConst {
12374 dst: reg(0),
12375 constant: cid(6),
12376 },
12377 Instruction::StoreGlobal {
12378 name: cid(1),
12379 value: reg(0),
12380 },
12381 Instruction::Return { value: reg(0) },
12382 ],
12383 Vec::new(),
12384 ),
12385 ],
12386 Vec::new(),
12387 vec![
12388 Binding {
12389 name: cid(1),
12390 kind: BindingKind::Hoisted,
12391 },
12392 Binding {
12393 name: cid(2),
12394 kind: BindingKind::Hoisted,
12395 },
12396 Binding {
12397 name: cid(3),
12398 kind: BindingKind::Hoisted,
12399 },
12400 ],
12401 vec![
12402 Export {
12403 name: cid(1),
12404 source: ExportSource::Local(BindingId::new(0)),
12405 },
12406 Export {
12407 name: cid(2),
12408 source: ExportSource::Local(BindingId::new(1)),
12409 },
12410 Export {
12411 name: cid(3),
12412 source: ExportSource::Local(BindingId::new(2)),
12413 },
12414 ],
12415 );
12416 let root = program_module(
12417 "root",
12418 vec![
12419 Constant::String(EcmaString::from_utf8("ns1")),
12420 Constant::String(EcmaString::from_utf8("ns2")),
12421 Constant::String(EcmaString::from_utf8("mutate")),
12422 Constant::String(EcmaString::from_utf8("z")),
12423 Constant::String(EcmaString::from_utf8("a")),
12424 Constant::String(EcmaString::from_utf8("dep")),
12425 Constant::String(EcmaString::from_utf8("Object")),
12426 Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
12427 Constant::String(EcmaString::from_utf8("value")),
12428 Constant::String(EcmaString::from_utf8("writable")),
12429 Constant::String(EcmaString::from_utf8("enumerable")),
12430 Constant::String(EcmaString::from_utf8("configurable")),
12431 Constant::String(EcmaString::from_utf8("missing")),
12432 ],
12433 vec![function(
12434 0,
12435 31,
12436 vec![
12437 Instruction::LoadGlobal {
12438 dst: reg(0),
12439 name: cid(1),
12440 },
12441 Instruction::LoadGlobal {
12442 dst: reg(1),
12443 name: cid(2),
12444 },
12445 Instruction::Binary {
12446 dst: reg(2),
12447 op: BinaryOp::StrictEqual,
12448 left: reg(0),
12449 right: reg(1),
12450 },
12451 Instruction::LoadGlobal {
12452 dst: reg(3),
12453 name: cid(3),
12454 },
12455 Instruction::CreateArray { dst: reg(4) },
12456 Instruction::Call {
12457 dst: reg(5),
12458 callee: reg(3),
12459 this_value: reg(4),
12460 arguments: reg(4),
12461 },
12462 Instruction::LoadConst {
12463 dst: reg(6),
12464 constant: cid(4),
12465 },
12466 Instruction::GetProperty {
12467 dst: reg(7),
12468 object: reg(0),
12469 key: reg(6),
12470 },
12471 Instruction::GetIterator {
12472 dst: reg(8),
12473 src: reg(0),
12474 kind: IteratorKind::Keys,
12475 },
12476 Instruction::IteratorNext {
12477 done: reg(9),
12478 value: reg(10),
12479 iterator: reg(8),
12480 },
12481 Instruction::LoadConst {
12482 dst: reg(11),
12483 constant: cid(5),
12484 },
12485 Instruction::Binary {
12486 dst: reg(12),
12487 op: BinaryOp::StrictEqual,
12488 left: reg(10),
12489 right: reg(11),
12490 },
12491 Instruction::IteratorNext {
12492 done: reg(9),
12493 value: reg(10),
12494 iterator: reg(8),
12495 },
12496 Instruction::LoadConst {
12497 dst: reg(13),
12498 constant: cid(3),
12499 },
12500 Instruction::Binary {
12501 dst: reg(5),
12502 op: BinaryOp::StrictEqual,
12503 left: reg(10),
12504 right: reg(13),
12505 },
12506 Instruction::IteratorNext {
12507 done: reg(9),
12508 value: reg(10),
12509 iterator: reg(8),
12510 },
12511 Instruction::Binary {
12512 dst: reg(14),
12513 op: BinaryOp::StrictEqual,
12514 left: reg(10),
12515 right: reg(6),
12516 },
12517 Instruction::LoadGlobal {
12518 dst: reg(15),
12519 name: cid(7),
12520 },
12521 Instruction::LoadConst {
12522 dst: reg(16),
12523 constant: cid(8),
12524 },
12525 Instruction::GetProperty {
12526 dst: reg(17),
12527 object: reg(15),
12528 key: reg(16),
12529 },
12530 Instruction::CreateArray { dst: reg(18) },
12531 Instruction::ArrayPush {
12532 array: reg(18),
12533 value: reg(0),
12534 },
12535 Instruction::ArrayPush {
12536 array: reg(18),
12537 value: reg(6),
12538 },
12539 Instruction::Call {
12540 dst: reg(19),
12541 callee: reg(17),
12542 this_value: reg(18),
12543 arguments: reg(18),
12544 },
12545 Instruction::LoadConst {
12546 dst: reg(20),
12547 constant: cid(9),
12548 },
12549 Instruction::GetProperty {
12550 dst: reg(21),
12551 object: reg(19),
12552 key: reg(20),
12553 },
12554 Instruction::LoadConst {
12555 dst: reg(22),
12556 constant: cid(10),
12557 },
12558 Instruction::GetProperty {
12559 dst: reg(23),
12560 object: reg(19),
12561 key: reg(22),
12562 },
12563 Instruction::LoadConst {
12564 dst: reg(24),
12565 constant: cid(11),
12566 },
12567 Instruction::GetProperty {
12568 dst: reg(25),
12569 object: reg(19),
12570 key: reg(24),
12571 },
12572 Instruction::LoadConst {
12573 dst: reg(26),
12574 constant: cid(12),
12575 },
12576 Instruction::GetProperty {
12577 dst: reg(27),
12578 object: reg(19),
12579 key: reg(26),
12580 },
12581 Instruction::CreateArray { dst: reg(28) },
12582 Instruction::LoadConst {
12583 dst: reg(29),
12584 constant: cid(13),
12585 },
12586 Instruction::ArrayPush {
12587 array: reg(28),
12588 value: reg(0),
12589 },
12590 Instruction::ArrayPush {
12591 array: reg(28),
12592 value: reg(29),
12593 },
12594 Instruction::Call {
12595 dst: reg(30),
12596 callee: reg(17),
12597 this_value: reg(28),
12598 arguments: reg(28),
12599 },
12600 Instruction::Return { value: reg(21) },
12601 ],
12602 Vec::new(),
12603 )],
12604 vec![Edge {
12605 specifier: cid(6),
12606 target: EdgeTarget::Local(ModuleId::new(0)),
12607 kind: EdgeKind::Static,
12608 }],
12609 vec![
12610 Binding {
12611 name: cid(1),
12612 kind: BindingKind::Namespace {
12613 edge: EdgeId::new(0),
12614 },
12615 },
12616 Binding {
12617 name: cid(2),
12618 kind: BindingKind::Namespace {
12619 edge: EdgeId::new(0),
12620 },
12621 },
12622 Binding {
12623 name: cid(3),
12624 kind: BindingKind::Imported {
12625 edge: EdgeId::new(0),
12626 name: cid(3),
12627 },
12628 },
12629 ],
12630 Vec::new(),
12631 );
12632 let execution = run_ok(&linked(vec![dependency, root], 1));
12633 assert_eq!(execution.value, Value::int32(3));
12634 assert_eq!(execution.entry_registers[2], Value::TRUE);
12635 assert_eq!(execution.entry_registers[5], Value::TRUE);
12636 assert_eq!(execution.entry_registers[12], Value::TRUE);
12637 assert_eq!(execution.entry_registers[14], Value::TRUE);
12638 assert_eq!(execution.entry_registers[23], Value::TRUE);
12639 assert_eq!(execution.entry_registers[25], Value::TRUE);
12640 assert_eq!(execution.entry_registers[27], Value::FALSE);
12641 assert_eq!(execution.entry_registers[30], Value::UNDEFINED);
12642 }
12643
12644 #[test]
12645 fn side_effect_module_runs_once_with_single_or_duplicate_static_edges() {
12646 for duplicate in [false, true] {
12647 let dependency = program_module(
12648 "dependency",
12649 vec![
12650 Constant::String(EcmaString::from_utf8("count")),
12651 Constant::Int32(0),
12652 Constant::Int32(1),
12653 ],
12654 vec![function(
12655 0,
12656 2,
12657 vec![
12658 Instruction::LoadGlobal {
12659 dst: reg(0),
12660 name: cid(1),
12661 },
12662 Instruction::JumpIfFalse {
12663 condition: reg(0),
12664 target: pc(3),
12665 },
12666 Instruction::Jump { target: pc(5) },
12667 Instruction::LoadConst {
12668 dst: reg(0),
12669 constant: cid(2),
12670 },
12671 Instruction::StoreGlobal {
12672 name: cid(1),
12673 value: reg(0),
12674 },
12675 Instruction::LoadConst {
12676 dst: reg(1),
12677 constant: cid(3),
12678 },
12679 Instruction::Binary {
12680 dst: reg(0),
12681 op: BinaryOp::Add,
12682 left: reg(0),
12683 right: reg(1),
12684 },
12685 Instruction::StoreGlobal {
12686 name: cid(1),
12687 value: reg(0),
12688 },
12689 Instruction::Return { value: reg(0) },
12690 ],
12691 Vec::new(),
12692 )],
12693 Vec::new(),
12694 vec![Binding {
12695 name: cid(1),
12696 kind: BindingKind::Hoisted,
12697 }],
12698 vec![Export {
12699 name: cid(1),
12700 source: ExportSource::Local(BindingId::new(0)),
12701 }],
12702 );
12703 let mut edges = vec![Edge {
12704 specifier: cid(2),
12705 target: EdgeTarget::Local(ModuleId::new(0)),
12706 kind: EdgeKind::Static,
12707 }];
12708 if duplicate {
12709 edges.push(Edge {
12710 specifier: cid(3),
12711 target: EdgeTarget::Local(ModuleId::new(0)),
12712 kind: EdgeKind::Static,
12713 });
12714 }
12715 let root = program_module(
12716 "root",
12717 vec![
12718 Constant::String(EcmaString::from_utf8("count")),
12719 Constant::String(EcmaString::from_utf8("dep-one")),
12720 Constant::String(EcmaString::from_utf8("dep-two")),
12721 ],
12722 vec![function(
12723 0,
12724 1,
12725 vec![
12726 Instruction::LoadGlobal {
12727 dst: reg(0),
12728 name: cid(1),
12729 },
12730 Instruction::Return { value: reg(0) },
12731 ],
12732 Vec::new(),
12733 )],
12734 edges,
12735 vec![Binding {
12736 name: cid(1),
12737 kind: BindingKind::Imported {
12738 edge: EdgeId::new(0),
12739 name: cid(1),
12740 },
12741 }],
12742 Vec::new(),
12743 );
12744 assert_eq!(
12745 run_ok(&linked(vec![dependency, root], 1)).value,
12746 Value::int32(1)
12747 );
12748 }
12749 }
12750
12751 #[test]
12752 fn failed_module_rethrows_the_identical_stored_value() {
12753 let module = program_module(
12754 "throws",
12755 Vec::new(),
12756 vec![function(
12757 0,
12758 1,
12759 vec![
12760 Instruction::CreateObject { dst: reg(0) },
12761 Instruction::Throw { value: reg(0) },
12762 ],
12763 Vec::new(),
12764 )],
12765 Vec::new(),
12766 Vec::new(),
12767 Vec::new(),
12768 );
12769 let program = linked(vec![module], 0);
12770 let mut host = TestHost;
12771 let mut machine = Machine::new(&program, &mut host, Limits::default());
12772 machine.frames.clear();
12773 machine.live_registers = 0;
12774 machine.instantiate_modules().unwrap();
12775 let first = machine.evaluate_module(ModuleId::new(0)).unwrap_err();
12776 let second = machine.evaluate_module(ModuleId::new(0)).unwrap_err();
12777 let RuntimeErrorKind::UncaughtThrow { value: first, .. } = first.kind else {
12778 panic!("module must fail by throwing");
12779 };
12780 let RuntimeErrorKind::UncaughtThrow { value: second, .. } = second.kind else {
12781 panic!("stored failure must remain a throw");
12782 };
12783 assert_eq!(first, second);
12784 assert!(first.as_heap_ref().is_some());
12785 }
12786
12787 #[test]
12788 fn external_static_edge_is_a_typed_runtime_error() {
12789 let module = program_module(
12790 "root",
12791 vec![Constant::String(EcmaString::from_utf8("external"))],
12792 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
12793 vec![Edge {
12794 specifier: cid(1),
12795 target: EdgeTarget::External,
12796 kind: EdgeKind::Static,
12797 }],
12798 Vec::new(),
12799 Vec::new(),
12800 );
12801 let program = linked(vec![module], 0);
12802 let mut host = TestHost;
12803 let error = Machine::new(&program, &mut host, Limits::default())
12804 .run()
12805 .unwrap_err();
12806 assert!(matches!(
12807 error.kind,
12808 RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
12809 if module == ModuleId::new(0) && edge == EdgeId::new(0)
12810 ));
12811 }
12812
12813 #[test]
12814 fn external_module_and_export_names_preserve_unicode() {
12815 for (specifier, export) in [("módulo", "value"), ("external", "café")] {
12816 let module = program_module(
12817 "root",
12818 vec![
12819 Constant::String(EcmaString::from_utf8(export)),
12820 Constant::String(EcmaString::from_utf8(specifier)),
12821 ],
12822 vec![function(
12823 0,
12824 1,
12825 vec![
12826 Instruction::LoadGlobal {
12827 dst: reg(0),
12828 name: cid(1),
12829 },
12830 Instruction::Return { value: reg(0) },
12831 ],
12832 Vec::new(),
12833 )],
12834 vec![Edge {
12835 specifier: cid(2),
12836 target: EdgeTarget::External,
12837 kind: EdgeKind::Static,
12838 }],
12839 vec![Binding {
12840 name: cid(1),
12841 kind: BindingKind::Imported {
12842 edge: EdgeId::new(0),
12843 name: cid(1),
12844 },
12845 }],
12846 Vec::new(),
12847 );
12848 let program = linked(vec![module], 0);
12849 let mut host = TestHost;
12850 let mut machine = Machine::new(&program, &mut host, Limits::default());
12851 machine.registry.external.insert(
12852 EcmaString::from_utf8(specifier),
12853 ExternalModuleInstance {
12854 namespace: Value::UNDEFINED,
12855 exports: BTreeMap::from([(
12856 EcmaString::from_utf8(export),
12857 ExternalExport {
12858 value: Value::int32(7),
12859 cell: None,
12860 },
12861 )]),
12862 internals: BTreeMap::new(),
12863 },
12864 );
12865
12866 assert_eq!(machine.run().unwrap().value, Value::int32(7));
12867 }
12868 }
12869
12870 #[test]
12871 fn dynamic_import_preserves_cycles_identity_and_single_evaluation() {
12872 let root = program_module(
12873 "root",
12874 vec![
12875 Constant::String(EcmaString::from_utf8("./dependency")),
12876 Constant::String(EcmaString::from_utf8("count")),
12877 Constant::Int32(0),
12878 Constant::String(EcmaString::from_utf8("value")),
12879 ],
12880 vec![function(
12881 0,
12882 7,
12883 vec![
12884 Instruction::LoadConst {
12885 dst: reg(0),
12886 constant: cid(3),
12887 },
12888 Instruction::StoreGlobal {
12889 name: cid(2),
12890 value: reg(0),
12891 },
12892 Instruction::Import {
12893 dst: reg(1),
12894 specifier: cid(1),
12895 },
12896 Instruction::Import {
12897 dst: reg(2),
12898 specifier: cid(1),
12899 },
12900 Instruction::Binary {
12901 dst: reg(3),
12902 op: BinaryOp::StrictEqual,
12903 left: reg(1),
12904 right: reg(2),
12905 },
12906 Instruction::LoadConst {
12907 dst: reg(4),
12908 constant: cid(4),
12909 },
12910 Instruction::GetProperty {
12911 dst: reg(5),
12912 object: reg(2),
12913 key: reg(4),
12914 },
12915 Instruction::LoadGlobal {
12916 dst: reg(6),
12917 name: cid(2),
12918 },
12919 Instruction::Return { value: reg(5) },
12920 ],
12921 Vec::new(),
12922 )],
12923 vec![Edge {
12924 specifier: cid(1),
12925 target: EdgeTarget::Local(ModuleId::new(1)),
12926 kind: EdgeKind::Dynamic,
12927 }],
12928 Vec::new(),
12929 Vec::new(),
12930 );
12931 let dependency = program_module(
12932 "dependency",
12933 vec![
12934 Constant::String(EcmaString::from_utf8("./root")),
12935 Constant::String(EcmaString::from_utf8("count")),
12936 Constant::Int32(1),
12937 Constant::Int32(7),
12938 Constant::String(EcmaString::from_utf8("value")),
12939 ],
12940 vec![function(
12941 0,
12942 3,
12943 vec![
12944 Instruction::LoadGlobal {
12945 dst: reg(0),
12946 name: cid(2),
12947 },
12948 Instruction::LoadConst {
12949 dst: reg(1),
12950 constant: cid(3),
12951 },
12952 Instruction::Binary {
12953 dst: reg(2),
12954 op: BinaryOp::Add,
12955 left: reg(0),
12956 right: reg(1),
12957 },
12958 Instruction::StoreGlobal {
12959 name: cid(2),
12960 value: reg(2),
12961 },
12962 Instruction::LoadConst {
12963 dst: reg(0),
12964 constant: cid(4),
12965 },
12966 Instruction::StoreGlobal {
12967 name: cid(5),
12968 value: reg(0),
12969 },
12970 Instruction::Return { value: reg(0) },
12971 ],
12972 Vec::new(),
12973 )],
12974 vec![Edge {
12975 specifier: cid(1),
12976 target: EdgeTarget::Local(ModuleId::new(0)),
12977 kind: EdgeKind::Static,
12978 }],
12979 vec![Binding {
12980 name: cid(5),
12981 kind: BindingKind::Hoisted,
12982 }],
12983 vec![Export {
12984 name: cid(5),
12985 source: ExportSource::Local(BindingId::new(0)),
12986 }],
12987 );
12988
12989 let execution = run_ok(&linked(vec![root, dependency], 0));
12990 assert_eq!(execution.value, Value::int32(7));
12991 assert_eq!(execution.entry_registers[1], execution.entry_registers[2]);
12992 assert_eq!(execution.entry_registers[3], Value::TRUE);
12993 assert_eq!(execution.entry_registers[6], Value::int32(1));
12994 }
12995
12996 #[test]
12997 fn dynamic_import_counts_live_registers_and_retries_engine_failures() {
12998 let target = program_module(
12999 "target",
13000 Vec::new(),
13001 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13002 Vec::new(),
13003 Vec::new(),
13004 Vec::new(),
13005 );
13006 let root = program_module(
13007 "root",
13008 vec![Constant::String(EcmaString::from_utf8("./target"))],
13009 vec![function(
13010 0,
13011 1,
13012 vec![
13013 Instruction::Import {
13014 dst: reg(0),
13015 specifier: cid(1),
13016 },
13017 Instruction::Return { value: reg(0) },
13018 ],
13019 Vec::new(),
13020 )],
13021 vec![Edge {
13022 specifier: cid(1),
13023 target: EdgeTarget::Local(ModuleId::new(1)),
13024 kind: EdgeKind::Dynamic,
13025 }],
13026 Vec::new(),
13027 Vec::new(),
13028 );
13029 let program = linked(vec![root, target], 0);
13030 let mut host = TestHost;
13031 let mut machine = Machine::new(
13032 &program,
13033 &mut host,
13034 Limits {
13035 max_total_registers: 1,
13036 ..Limits::default()
13037 },
13038 );
13039 machine.frames.clear();
13040 machine.live_registers = 0;
13041 machine.instantiate_modules().unwrap();
13042
13043 let error = machine.evaluate_import(ModuleId::new(0)).unwrap_err();
13044 assert!(matches!(
13045 error.kind,
13046 RuntimeErrorKind::RegisterLimitExceeded { limit: 1 }
13047 ));
13048 assert_eq!(machine.frames.len(), 0);
13049 assert_eq!(machine.live_registers, 0);
13050
13051 machine.limits.max_total_registers = 2;
13052 machine.evaluate_import(ModuleId::new(0)).unwrap();
13053 }
13054
13055 #[test]
13056 fn dynamic_import_rethrows_one_stored_failure_at_each_import_site() {
13057 let root = program_module(
13058 "root",
13059 vec![
13060 Constant::String(EcmaString::from_utf8("./target")),
13061 Constant::String(EcmaString::from_utf8("count")),
13062 Constant::Int32(0),
13063 ],
13064 vec![function(
13065 0,
13066 4,
13067 vec![
13068 Instruction::LoadConst {
13069 dst: reg(0),
13070 constant: cid(3),
13071 },
13072 Instruction::StoreGlobal {
13073 name: cid(2),
13074 value: reg(0),
13075 },
13076 Instruction::Import {
13077 dst: reg(0),
13078 specifier: cid(1),
13079 },
13080 Instruction::Halt,
13081 Instruction::Import {
13082 dst: reg(0),
13083 specifier: cid(1),
13084 },
13085 Instruction::Halt,
13086 Instruction::LoadGlobal {
13087 dst: reg(3),
13088 name: cid(2),
13089 },
13090 Instruction::Return { value: reg(2) },
13091 ],
13092 vec![
13093 ExceptionHandler {
13094 start: pc(2),
13095 end: pc(3),
13096 handler: pc(4),
13097 catch_register: reg(1),
13098 },
13099 ExceptionHandler {
13100 start: pc(4),
13101 end: pc(5),
13102 handler: pc(6),
13103 catch_register: reg(2),
13104 },
13105 ],
13106 )],
13107 vec![Edge {
13108 specifier: cid(1),
13109 target: EdgeTarget::Local(ModuleId::new(1)),
13110 kind: EdgeKind::Dynamic,
13111 }],
13112 Vec::new(),
13113 Vec::new(),
13114 );
13115 let target = program_module(
13116 "target",
13117 vec![
13118 Constant::String(EcmaString::from_utf8("count")),
13119 Constant::Int32(1),
13120 Constant::Int32(9),
13121 ],
13122 vec![function(
13123 0,
13124 3,
13125 vec![
13126 Instruction::LoadGlobal {
13127 dst: reg(0),
13128 name: cid(1),
13129 },
13130 Instruction::LoadConst {
13131 dst: reg(1),
13132 constant: cid(2),
13133 },
13134 Instruction::Binary {
13135 dst: reg(2),
13136 op: BinaryOp::Add,
13137 left: reg(0),
13138 right: reg(1),
13139 },
13140 Instruction::StoreGlobal {
13141 name: cid(1),
13142 value: reg(2),
13143 },
13144 Instruction::LoadConst {
13145 dst: reg(0),
13146 constant: cid(3),
13147 },
13148 Instruction::Throw { value: reg(0) },
13149 ],
13150 Vec::new(),
13151 )],
13152 Vec::new(),
13153 Vec::new(),
13154 Vec::new(),
13155 );
13156
13157 let execution = run_ok(&linked(vec![root, target], 0));
13158 assert_eq!(execution.value, Value::int32(9));
13159 assert_eq!(execution.entry_registers[1], Value::int32(9));
13160 assert_eq!(execution.entry_registers[2], Value::int32(9));
13161 assert_eq!(execution.entry_registers[3], Value::int32(1));
13162 }
13163
13164 #[test]
13165 fn dynamic_import_returns_the_registered_external_namespace() {
13166 let module = program_module(
13167 "root",
13168 vec![Constant::String(EcmaString::from_utf8("external"))],
13169 vec![function(
13170 0,
13171 3,
13172 vec![
13173 Instruction::Import {
13174 dst: reg(0),
13175 specifier: cid(1),
13176 },
13177 Instruction::Import {
13178 dst: reg(1),
13179 specifier: cid(1),
13180 },
13181 Instruction::Binary {
13182 dst: reg(2),
13183 op: BinaryOp::StrictEqual,
13184 left: reg(0),
13185 right: reg(1),
13186 },
13187 Instruction::Return { value: reg(2) },
13188 ],
13189 Vec::new(),
13190 )],
13191 vec![Edge {
13192 specifier: cid(1),
13193 target: EdgeTarget::External,
13194 kind: EdgeKind::Dynamic,
13195 }],
13196 Vec::new(),
13197 Vec::new(),
13198 );
13199 let program = linked(vec![module], 0);
13200 let mut host = TestHost;
13201 let mut machine = Machine::new(&program, &mut host, Limits::default());
13202 let namespace = machine
13203 .allocate(HeapEntry::Object {
13204 properties: PropertyMap::default(),
13205 prototype: Some(machine.intrinsics.object_prototype),
13206 boxed_primitive: None,
13207 extensible: true,
13208 })
13209 .unwrap();
13210 machine.registry.external.insert(
13211 EcmaString::from_utf8("external"),
13212 ExternalModuleInstance {
13213 namespace,
13214 exports: BTreeMap::new(),
13215 internals: BTreeMap::new(),
13216 },
13217 );
13218
13219 let execution = machine.run().unwrap();
13220 assert_eq!(execution.value, Value::TRUE);
13221 assert_eq!(execution.entry_registers[0], namespace);
13222 assert_eq!(execution.entry_registers[1], namespace);
13223 }
13224
13225 #[test]
13226 fn dynamic_import_resolution_is_requester_scoped() {
13227 let requester = |name, target| {
13228 program_module(
13229 name,
13230 vec![Constant::String(EcmaString::from_utf8("./target"))],
13231 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13232 vec![Edge {
13233 specifier: cid(1),
13234 target: EdgeTarget::Local(ModuleId::new(target)),
13235 kind: EdgeKind::Dynamic,
13236 }],
13237 Vec::new(),
13238 Vec::new(),
13239 )
13240 };
13241 let target = |name| {
13242 program_module(
13243 name,
13244 Vec::new(),
13245 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13246 Vec::new(),
13247 Vec::new(),
13248 Vec::new(),
13249 )
13250 };
13251 let program = linked(
13252 vec![
13253 requester("first", 2),
13254 requester("second", 3),
13255 target("first-target"),
13256 target("second-target"),
13257 ],
13258 0,
13259 );
13260 let mut host = TestHost;
13261 let machine = Machine::new(&program, &mut host, Limits::default());
13262
13263 assert_eq!(
13264 machine.resolve_import(ModuleId::new(0), cid(1)),
13265 Ok(ImportTarget::Local(ModuleId::new(2)))
13266 );
13267 assert_eq!(
13268 machine.resolve_import(ModuleId::new(1), cid(1)),
13269 Ok(ImportTarget::Local(ModuleId::new(3)))
13270 );
13271 }
13272
13273 #[test]
13274 fn dynamic_import_of_a_missing_external_is_a_runtime_error() {
13275 let module = program_module(
13276 "root",
13277 vec![Constant::String(EcmaString::from_utf8("dynamic"))],
13278 vec![function(
13279 0,
13280 1,
13281 vec![
13282 Instruction::Import {
13283 dst: reg(0),
13284 specifier: cid(1),
13285 },
13286 Instruction::Return { value: reg(0) },
13287 ],
13288 Vec::new(),
13289 )],
13290 vec![Edge {
13291 specifier: cid(1),
13292 target: EdgeTarget::External,
13293 kind: EdgeKind::Dynamic,
13294 }],
13295 Vec::new(),
13296 Vec::new(),
13297 );
13298 let program = linked(vec![module], 0);
13299 let mut host = TestHost;
13300 let error = Machine::new(&program, &mut host, Limits::default())
13301 .run()
13302 .unwrap_err();
13303 assert!(matches!(
13304 error.kind,
13305 RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
13306 if module == ModuleId::new(0) && edge == EdgeId::new(0)
13307 ));
13308 }
13309
13310 #[test]
13311 fn unbound_global_names_fall_back_to_the_realm_global_map() {
13312 let program = verified(
13313 vec![
13314 Constant::String(EcmaString::from_utf8("realmOnly")),
13315 Constant::Int32(7),
13316 ],
13317 vec![function(
13318 0,
13319 1,
13320 vec![
13321 Instruction::LoadConst {
13322 dst: reg(0),
13323 constant: cid(1),
13324 },
13325 Instruction::StoreGlobal {
13326 name: cid(0),
13327 value: reg(0),
13328 },
13329 Instruction::LoadGlobal {
13330 dst: reg(0),
13331 name: cid(0),
13332 },
13333 Instruction::Return { value: reg(0) },
13334 ],
13335 Vec::new(),
13336 )],
13337 );
13338 assert_eq!(run_ok(&program).value, Value::int32(7));
13339 }
13340
13341 #[test]
13342 fn module_cell_limit_is_enforced_before_evaluation() {
13343 let module = program_module(
13344 "root",
13345 vec![Constant::String(EcmaString::from_utf8("x"))],
13346 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13347 Vec::new(),
13348 vec![Binding {
13349 name: cid(1),
13350 kind: BindingKind::Hoisted,
13351 }],
13352 Vec::new(),
13353 );
13354 let program = linked(vec![module], 0);
13355 let mut host = TestHost;
13356 let error = Machine::new(
13357 &program,
13358 &mut host,
13359 Limits {
13360 max_module_cells: 0,
13361 ..Limits::default()
13362 },
13363 )
13364 .run()
13365 .unwrap_err();
13366 assert!(matches!(
13367 error.kind,
13368 RuntimeErrorKind::ModuleCellLimitExceeded { limit: 0 }
13369 ));
13370 }
13371 #[test]
13372 fn imported_binding_store_throws_without_mutating_the_exporter() {
13373 let dependency = program_module(
13374 "dependency",
13375 vec![
13376 Constant::String(EcmaString::from_utf8("x")),
13377 Constant::Int32(1),
13378 ],
13379 vec![function(
13380 0,
13381 1,
13382 vec![
13383 Instruction::LoadConst {
13384 dst: reg(0),
13385 constant: cid(2),
13386 },
13387 Instruction::StoreGlobal {
13388 name: cid(1),
13389 value: reg(0),
13390 },
13391 Instruction::Return { value: reg(0) },
13392 ],
13393 Vec::new(),
13394 )],
13395 Vec::new(),
13396 vec![Binding {
13397 name: cid(1),
13398 kind: BindingKind::Hoisted,
13399 }],
13400 vec![Export {
13401 name: cid(1),
13402 source: ExportSource::Local(BindingId::new(0)),
13403 }],
13404 );
13405 let root = program_module(
13406 "root",
13407 vec![
13408 Constant::String(EcmaString::from_utf8("x")),
13409 Constant::Int32(2),
13410 Constant::String(EcmaString::from_utf8("dep")),
13411 ],
13412 vec![function(
13413 0,
13414 1,
13415 vec![
13416 Instruction::LoadConst {
13417 dst: reg(0),
13418 constant: cid(2),
13419 },
13420 Instruction::StoreGlobal {
13421 name: cid(1),
13422 value: reg(0),
13423 },
13424 Instruction::Return { value: reg(0) },
13425 ],
13426 Vec::new(),
13427 )],
13428 vec![Edge {
13429 specifier: cid(3),
13430 target: EdgeTarget::Local(ModuleId::new(0)),
13431 kind: EdgeKind::Static,
13432 }],
13433 vec![Binding {
13434 name: cid(1),
13435 kind: BindingKind::Imported {
13436 edge: EdgeId::new(0),
13437 name: cid(1),
13438 },
13439 }],
13440 Vec::new(),
13441 );
13442 let program = linked(vec![dependency, root], 1);
13443 let mut host = TestHost;
13444 let mut machine = Machine::new(&program, &mut host, Limits::default());
13445 machine.frames.clear();
13446 machine.live_registers = 0;
13447 machine.instantiate_modules().unwrap();
13448 assert!(machine.evaluate_module(ModuleId::new(1)).is_err());
13449 let exporter = machine.registry.modules[0].binding_cells[0].unwrap();
13450 assert_eq!(machine.registry.cells[exporter.0].value, Value::int32(1));
13451 }
13452
13453 #[test]
13454 fn namespace_descriptor_propagates_temporal_dead_zone() {
13455 let root = program_module(
13456 "root",
13457 vec![
13458 Constant::String(EcmaString::from_utf8("x")),
13459 Constant::Int32(1),
13460 Constant::String(EcmaString::from_utf8("dependency")),
13461 ],
13462 vec![function(
13463 0,
13464 1,
13465 vec![
13466 Instruction::LoadConst {
13467 dst: reg(0),
13468 constant: cid(2),
13469 },
13470 Instruction::StoreGlobal {
13471 name: cid(1),
13472 value: reg(0),
13473 },
13474 Instruction::Return { value: reg(0) },
13475 ],
13476 Vec::new(),
13477 )],
13478 vec![Edge {
13479 specifier: cid(3),
13480 target: EdgeTarget::Local(ModuleId::new(1)),
13481 kind: EdgeKind::Static,
13482 }],
13483 vec![Binding {
13484 name: cid(1),
13485 kind: BindingKind::Lexical,
13486 }],
13487 vec![Export {
13488 name: cid(1),
13489 source: ExportSource::Local(BindingId::new(0)),
13490 }],
13491 );
13492 let dependency = program_module(
13493 "dependency",
13494 vec![
13495 Constant::String(EcmaString::from_utf8("ns")),
13496 Constant::String(EcmaString::from_utf8("root")),
13497 Constant::String(EcmaString::from_utf8("Object")),
13498 Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
13499 Constant::String(EcmaString::from_utf8("x")),
13500 ],
13501 vec![namespace_descriptor_entry()],
13502 vec![Edge {
13503 specifier: cid(2),
13504 target: EdgeTarget::Local(ModuleId::new(0)),
13505 kind: EdgeKind::Static,
13506 }],
13507 vec![Binding {
13508 name: cid(1),
13509 kind: BindingKind::Namespace {
13510 edge: EdgeId::new(0),
13511 },
13512 }],
13513 Vec::new(),
13514 );
13515 let program = linked(vec![root, dependency], 0);
13516 let mut host = TestHost;
13517 let error = Machine::new(&program, &mut host, Limits::default())
13518 .run()
13519 .expect_err("descriptor reads uninitialized namespace export");
13520 assert!(matches!(
13521 error.kind,
13522 RuntimeErrorKind::TemporalDeadZone { module, binding }
13523 if module == ModuleId::new(0) && binding == BindingId::new(0)
13524 ));
13525 }
13526
13527 #[test]
13528 fn namespace_descriptor_propagates_external_linkage_error() {
13529 let exported = program_module(
13530 "exported",
13531 vec![
13532 Constant::String(EcmaString::from_utf8("x")),
13533 Constant::String(EcmaString::from_utf8("external")),
13534 ],
13535 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13536 vec![Edge {
13537 specifier: cid(2),
13538 target: EdgeTarget::External,
13539 kind: EdgeKind::Dynamic,
13540 }],
13541 Vec::new(),
13542 vec![Export {
13543 name: cid(1),
13544 source: ExportSource::Indirect {
13545 edge: EdgeId::new(0),
13546 name: cid(1),
13547 },
13548 }],
13549 );
13550 let importer = program_module(
13551 "importer",
13552 vec![
13553 Constant::String(EcmaString::from_utf8("ns")),
13554 Constant::String(EcmaString::from_utf8("exported")),
13555 Constant::String(EcmaString::from_utf8("Object")),
13556 Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
13557 Constant::String(EcmaString::from_utf8("x")),
13558 ],
13559 vec![namespace_descriptor_entry()],
13560 vec![Edge {
13561 specifier: cid(2),
13562 target: EdgeTarget::Local(ModuleId::new(0)),
13563 kind: EdgeKind::Static,
13564 }],
13565 vec![Binding {
13566 name: cid(1),
13567 kind: BindingKind::Namespace {
13568 edge: EdgeId::new(0),
13569 },
13570 }],
13571 Vec::new(),
13572 );
13573 let program = linked(vec![exported, importer], 1);
13574 let mut host = TestHost;
13575 let error = Machine::new(&program, &mut host, Limits::default())
13576 .run()
13577 .expect_err("descriptor resolves external namespace export");
13578 assert!(matches!(
13579 error.kind,
13580 RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
13581 if module == ModuleId::new(0) && edge == EdgeId::new(0)
13582 ));
13583 }
13584
13585 #[test]
13586 fn installed_script_uses_machine_wide_id_and_keeps_its_code() {
13587 let root = verified(
13588 Vec::new(),
13589 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13590 );
13591 let script = Arc::new(verified(
13592 vec![Constant::Int32(42)],
13593 vec![function(
13594 0,
13595 1,
13596 vec![
13597 Instruction::LoadConst {
13598 dst: reg(0),
13599 constant: cid(0),
13600 },
13601 Instruction::Return { value: reg(0) },
13602 ],
13603 Vec::new(),
13604 )],
13605 ));
13606 let mut host = TestHost;
13607 let mut machine = Machine::new(&root, &mut host, Limits::default());
13608 machine.instantiate_modules().unwrap();
13609 let module = machine.install_script_reserving(script, 0, 0).unwrap();
13610
13611 assert_eq!(module, ModuleId::new(root.modules().len() as u32));
13612 assert!(machine.program().module(module).is_none());
13613 assert_eq!(
13614 machine.module_code(module).constants()[0],
13615 Constant::Int32(42)
13616 );
13617
13618 let closure = machine
13619 .allocate(HeapEntry::Function {
13620 module,
13621 function: FunctionId::new(0),
13622 captures: Vec::new(),
13623 properties: PropertyMap::default(),
13624 prototype: Some(machine.intrinsics.function_prototype),
13625 extensible: true,
13626 })
13627 .unwrap();
13628 assert!(matches!(
13629 machine.call_value(closure, Value::UNDEFINED, &[]),
13630 Ok(value) if value == Value::int32(42)
13631 ));
13632 }
13633
13634 #[test]
13635 fn installed_script_rejects_non_classic_programs_and_enforces_limit() {
13636 let root = verified(
13637 Vec::new(),
13638 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13639 );
13640 let two_modules = Arc::new(linked(
13641 vec![
13642 program_module(
13643 "first",
13644 Vec::new(),
13645 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13646 Vec::new(),
13647 Vec::new(),
13648 Vec::new(),
13649 ),
13650 program_module(
13651 "second",
13652 Vec::new(),
13653 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13654 Vec::new(),
13655 Vec::new(),
13656 Vec::new(),
13657 ),
13658 ],
13659 0,
13660 ));
13661 let script = Arc::new(verified(
13662 Vec::new(),
13663 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13664 ));
13665 let mut host = TestHost;
13666 let mut machine = Machine::new(
13667 &root,
13668 &mut host,
13669 Limits {
13670 max_dynamic_modules: 1,
13671 ..Limits::default()
13672 },
13673 );
13674 machine.instantiate_modules().unwrap();
13675
13676 assert!(matches!(
13677 machine.install_script_reserving(two_modules, 0, 0),
13678 Err(RuntimeErrorKind::InvalidDynamicScript { .. })
13679 ));
13680 machine
13681 .install_script_reserving(script.clone(), 0, 0)
13682 .unwrap();
13683 assert!(matches!(
13684 machine.install_script_reserving(script, 0, 0),
13685 Err(RuntimeErrorKind::DynamicModuleLimitExceeded { limit: 1 })
13686 ));
13687 }
13688
13689 #[test]
13690 fn script_heap_cost_counts_scalar_constant_slots() {
13691 let entry = || vec![function(0, 1, vec![Instruction::Halt], Vec::new())];
13692 let empty = verified(Vec::new(), entry());
13693 let constants = vec![Constant::Int32(0); 128];
13694 let scalars = verified(constants.clone(), entry());
13695
13696 let added = Machine::<TestHost>::script_heap_cost(&scalars)
13697 - Machine::<TestHost>::script_heap_cost(&empty);
13698
13699 assert!(added >= constants.len() * std::mem::size_of::<Constant>());
13700 }
13701
13702 #[test]
13703 fn script_heap_cost_includes_verification_storage() {
13704 let small = verified(
13705 Vec::new(),
13706 vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13707 );
13708 let large = verified(
13709 Vec::new(),
13710 vec![function(0, 130, vec![Instruction::Halt], Vec::new())],
13711 );
13712 let small_verification = small.modules()[0].code.verification_bytes();
13713 let large_verification = large.modules()[0].code.verification_bytes();
13714
13715 assert_eq!(
13716 Machine::<TestHost>::script_heap_cost(&large)
13717 - Machine::<TestHost>::script_heap_cost(&small),
13718 large_verification - small_verification
13719 );
13720 }
13721 #[test]
13722 fn promise_resolver_settles_once_and_reactions_wait_for_drain() {
13723 let program = verified(
13724 vec![
13725 Constant::String(EcmaString::from_utf8("resolve")),
13726 Constant::String(EcmaString::from_utf8("reject")),
13727 Constant::String(EcmaString::from_utf8("observed")),
13728 ],
13729 vec![
13730 function(0, 1, vec![Instruction::Halt], Vec::new()),
13731 function(
13732 2,
13733 2,
13734 vec![
13735 Instruction::StoreGlobal {
13736 name: cid(0),
13737 value: reg(0),
13738 },
13739 Instruction::StoreGlobal {
13740 name: cid(1),
13741 value: reg(1),
13742 },
13743 Instruction::Return { value: reg(0) },
13744 ],
13745 Vec::new(),
13746 ),
13747 function(
13748 1,
13749 1,
13750 vec![
13751 Instruction::StoreGlobal {
13752 name: cid(2),
13753 value: reg(0),
13754 },
13755 Instruction::Return { value: reg(0) },
13756 ],
13757 Vec::new(),
13758 ),
13759 ],
13760 );
13761 let mut host = TestHost;
13762 let mut machine = Machine::new(&program, &mut host, Limits::default());
13763 machine.frames.clear();
13764 machine.live_registers = 0;
13765 let executor = machine
13766 .allocate(HeapEntry::Function {
13767 module: ModuleId::new(0),
13768 function: FunctionId::new(1),
13769 captures: Vec::new(),
13770 properties: PropertyMap::default(),
13771 prototype: Some(machine.intrinsics.function_prototype),
13772 extensible: true,
13773 })
13774 .unwrap();
13775 let observer = machine
13776 .allocate(HeapEntry::Function {
13777 module: ModuleId::new(0),
13778 function: FunctionId::new(2),
13779 captures: Vec::new(),
13780 properties: PropertyMap::default(),
13781 prototype: Some(machine.intrinsics.function_prototype),
13782 extensible: true,
13783 })
13784 .unwrap();
13785 let constructor = machine.intrinsics.global("Promise").unwrap();
13786 let constructor_index = machine.runtime_slot(constructor).unwrap().unwrap();
13787 let HeapEntry::NativeFunction {
13788 callable: NativeCallable::Builtin(constructor_id),
13789 ..
13790 } = machine.heap[constructor_index]
13791 else {
13792 panic!("Promise must be a native constructor");
13793 };
13794 let BuiltinOutcome::Value(promise) = machine
13795 .call_builtin(constructor_id, Value::UNDEFINED, &[executor], true)
13796 .unwrap()
13797 else {
13798 panic!("Promise construction returns a Promise");
13799 };
13800 let then = machine.get_named_property(promise, "then").unwrap();
13801 machine
13802 .call_value(then, promise, &[observer])
13803 .expect("then returns a derived Promise");
13804 let resolve = machine
13805 .globals
13806 .get(&EcmaString::from_utf8("resolve"))
13807 .copied()
13808 .unwrap();
13809 let reject = machine
13810 .globals
13811 .get(&EcmaString::from_utf8("reject"))
13812 .copied()
13813 .unwrap();
13814 assert_eq!(
13815 machine
13816 .call_value(resolve, Value::UNDEFINED, &[Value::int32(1)])
13817 .unwrap(),
13818 Value::UNDEFINED
13819 );
13820 assert_eq!(
13821 machine
13822 .call_value(reject, Value::UNDEFINED, &[Value::int32(2)])
13823 .unwrap(),
13824 Value::UNDEFINED
13825 );
13826 assert!(
13827 !machine
13828 .globals
13829 .contains_key(&EcmaString::from_utf8("observed"))
13830 );
13831
13832 let drain = machine.drain_microtasks().unwrap();
13833 assert_eq!(drain.executed, 1);
13834 assert!(drain.uncaught.is_empty());
13835 assert_eq!(
13836 machine
13837 .globals
13838 .get(&EcmaString::from_utf8("observed"))
13839 .copied(),
13840 Some(Value::int32(1))
13841 );
13842 }
13843
13844 #[test]
13845 fn promise_resolution_adopts_thenables_with_a_fresh_resolver() {
13846 let program = verified(
13847 vec![
13848 Constant::String(EcmaString::from_utf8("resolve")),
13849 Constant::String(EcmaString::from_utf8("reject")),
13850 Constant::String(EcmaString::from_utf8("observed")),
13851 Constant::Int32(7),
13852 Constant::Int32(8),
13853 Constant::Int32(9),
13854 Constant::Undefined,
13855 ],
13856 vec![
13857 function(0, 1, vec![Instruction::Halt], Vec::new()),
13858 function(
13859 2,
13860 2,
13861 vec![
13862 Instruction::StoreGlobal {
13863 name: cid(0),
13864 value: reg(0),
13865 },
13866 Instruction::StoreGlobal {
13867 name: cid(1),
13868 value: reg(1),
13869 },
13870 Instruction::Return { value: reg(0) },
13871 ],
13872 Vec::new(),
13873 ),
13874 function(
13875 1,
13876 1,
13877 vec![
13878 Instruction::StoreGlobal {
13879 name: cid(2),
13880 value: reg(0),
13881 },
13882 Instruction::Return { value: reg(0) },
13883 ],
13884 Vec::new(),
13885 ),
13886 function(
13887 2,
13888 6,
13889 vec![
13890 Instruction::LoadConst {
13891 dst: reg(2),
13892 constant: cid(3),
13893 },
13894 Instruction::CreateArray { dst: reg(3) },
13895 Instruction::ArrayPush {
13896 array: reg(3),
13897 value: reg(2),
13898 },
13899 Instruction::LoadConst {
13900 dst: reg(4),
13901 constant: cid(6),
13902 },
13903 Instruction::Call {
13904 dst: reg(5),
13905 callee: reg(0),
13906 this_value: reg(4),
13907 arguments: reg(3),
13908 },
13909 Instruction::LoadConst {
13910 dst: reg(2),
13911 constant: cid(4),
13912 },
13913 Instruction::CreateArray { dst: reg(3) },
13914 Instruction::ArrayPush {
13915 array: reg(3),
13916 value: reg(2),
13917 },
13918 Instruction::Call {
13919 dst: reg(5),
13920 callee: reg(1),
13921 this_value: reg(4),
13922 arguments: reg(3),
13923 },
13924 Instruction::LoadConst {
13925 dst: reg(2),
13926 constant: cid(5),
13927 },
13928 Instruction::Throw { value: reg(2) },
13929 ],
13930 Vec::new(),
13931 ),
13932 ],
13933 );
13934 let mut host = TestHost;
13935 let mut machine = Machine::new(&program, &mut host, Limits::default());
13936 machine.frames.clear();
13937 machine.live_registers = 0;
13938 let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
13939 machine
13940 .allocate(HeapEntry::Function {
13941 module: ModuleId::new(0),
13942 function: FunctionId::new(function),
13943 captures: Vec::new(),
13944 properties: PropertyMap::default(),
13945 prototype: Some(machine.intrinsics.function_prototype),
13946 extensible: true,
13947 })
13948 .unwrap()
13949 };
13950 let executor = runtime_function(&mut machine, 1);
13951 let observer = runtime_function(&mut machine, 2);
13952 let then_callback = runtime_function(&mut machine, 3);
13953 let thenable = machine
13954 .allocate(HeapEntry::Object {
13955 properties: PropertyMap::default(),
13956 prototype: Some(machine.intrinsics.object_prototype),
13957 boxed_primitive: None,
13958 extensible: true,
13959 })
13960 .unwrap();
13961 machine
13962 .set_data_property(thenable, "then", then_callback)
13963 .unwrap();
13964
13965 let constructor = machine.intrinsics.global("Promise").unwrap();
13966 let constructor_index = machine.runtime_slot(constructor).unwrap().unwrap();
13967 let HeapEntry::NativeFunction {
13968 callable: NativeCallable::Builtin(constructor_id),
13969 ..
13970 } = machine.heap[constructor_index]
13971 else {
13972 panic!("Promise must be a native constructor");
13973 };
13974 let BuiltinOutcome::Value(promise) = machine
13975 .call_builtin(constructor_id, Value::UNDEFINED, &[executor], true)
13976 .unwrap()
13977 else {
13978 panic!("Promise construction returns a Promise");
13979 };
13980 let resolve = machine
13981 .globals
13982 .get(&EcmaString::from_utf8("resolve"))
13983 .copied()
13984 .unwrap();
13985 let reject = machine
13986 .globals
13987 .get(&EcmaString::from_utf8("reject"))
13988 .copied()
13989 .unwrap();
13990 machine
13991 .call_value(resolve, Value::UNDEFINED, &[thenable])
13992 .unwrap();
13993 let then = machine.get_named_property(promise, "then").unwrap();
13994 machine.call_value(then, promise, &[observer]).unwrap();
13995 machine
13996 .call_value(reject, Value::UNDEFINED, &[Value::int32(9)])
13997 .unwrap();
13998 assert!(
13999 !machine
14000 .globals
14001 .contains_key(&EcmaString::from_utf8("observed"))
14002 );
14003
14004 let drain = machine.drain_microtasks().unwrap();
14005 assert_eq!(drain.executed, 2);
14006 assert!(drain.uncaught.is_empty());
14007 assert_eq!(
14008 machine
14009 .globals
14010 .get(&EcmaString::from_utf8("observed"))
14011 .copied(),
14012 Some(Value::int32(7))
14013 );
14014 }
14015
14016 #[test]
14017 fn queue_microtask_drains_fifo_including_jobs_added_during_drain() {
14018 let program = verified(
14019 vec![
14020 Constant::String(EcmaString::from_utf8("order")),
14021 Constant::String(EcmaString::from_utf8("queueMicrotask")),
14022 Constant::String(EcmaString::from_utf8("third")),
14023 Constant::Int32(1),
14024 Constant::Int32(2),
14025 Constant::Int32(3),
14026 Constant::Undefined,
14027 ],
14028 vec![
14029 function(0, 1, vec![Instruction::Halt], Vec::new()),
14030 function(
14031 0,
14032 7,
14033 vec![
14034 Instruction::LoadGlobal {
14035 dst: reg(0),
14036 name: cid(0),
14037 },
14038 Instruction::LoadConst {
14039 dst: reg(1),
14040 constant: cid(3),
14041 },
14042 Instruction::ArrayPush {
14043 array: reg(0),
14044 value: reg(1),
14045 },
14046 Instruction::LoadGlobal {
14047 dst: reg(2),
14048 name: cid(1),
14049 },
14050 Instruction::LoadGlobal {
14051 dst: reg(3),
14052 name: cid(2),
14053 },
14054 Instruction::CreateArray { dst: reg(4) },
14055 Instruction::ArrayPush {
14056 array: reg(4),
14057 value: reg(3),
14058 },
14059 Instruction::LoadConst {
14060 dst: reg(5),
14061 constant: cid(6),
14062 },
14063 Instruction::Call {
14064 dst: reg(6),
14065 callee: reg(2),
14066 this_value: reg(5),
14067 arguments: reg(4),
14068 },
14069 Instruction::Return { value: reg(1) },
14070 ],
14071 Vec::new(),
14072 ),
14073 function(
14074 0,
14075 2,
14076 vec![
14077 Instruction::LoadGlobal {
14078 dst: reg(0),
14079 name: cid(0),
14080 },
14081 Instruction::LoadConst {
14082 dst: reg(1),
14083 constant: cid(4),
14084 },
14085 Instruction::ArrayPush {
14086 array: reg(0),
14087 value: reg(1),
14088 },
14089 Instruction::Return { value: reg(1) },
14090 ],
14091 Vec::new(),
14092 ),
14093 function(
14094 0,
14095 2,
14096 vec![
14097 Instruction::LoadGlobal {
14098 dst: reg(0),
14099 name: cid(0),
14100 },
14101 Instruction::LoadConst {
14102 dst: reg(1),
14103 constant: cid(5),
14104 },
14105 Instruction::ArrayPush {
14106 array: reg(0),
14107 value: reg(1),
14108 },
14109 Instruction::Return { value: reg(1) },
14110 ],
14111 Vec::new(),
14112 ),
14113 ],
14114 );
14115 let mut host = TestHost;
14116 let mut machine = Machine::new(&program, &mut host, Limits::default());
14117 machine.frames.clear();
14118 machine.live_registers = 0;
14119 let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
14120 machine
14121 .allocate(HeapEntry::Function {
14122 module: ModuleId::new(0),
14123 function: FunctionId::new(function),
14124 captures: Vec::new(),
14125 properties: PropertyMap::default(),
14126 prototype: Some(machine.intrinsics.function_prototype),
14127 extensible: true,
14128 })
14129 .unwrap()
14130 };
14131 let first = runtime_function(&mut machine, 1);
14132 let second = runtime_function(&mut machine, 2);
14133 let third = runtime_function(&mut machine, 3);
14134 let order = machine
14135 .allocate(HeapEntry::Array {
14136 elements: Vec::new(),
14137 properties: PropertyMap::default(),
14138 prototype: Some(machine.intrinsics.array_prototype),
14139 extensible: true,
14140 length_writable: true,
14141 })
14142 .unwrap();
14143 machine
14144 .globals
14145 .insert(EcmaString::from_utf8("order"), order);
14146 machine
14147 .globals
14148 .insert(EcmaString::from_utf8("third"), third);
14149 let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14150 machine
14151 .call_value(queue, Value::UNDEFINED, &[first])
14152 .unwrap();
14153 machine
14154 .call_value(queue, Value::UNDEFINED, &[second])
14155 .unwrap();
14156
14157 let drain = machine.drain_microtasks().unwrap();
14158 assert_eq!(drain.executed, 3);
14159 assert!(drain.uncaught.is_empty());
14160 let index = machine.runtime_slot(order).unwrap().unwrap();
14161 let HeapEntry::Array { elements, .. } = &machine.heap[index] else {
14162 panic!("order remains an array");
14163 };
14164 assert_eq!(
14165 elements,
14166 &[Value::int32(1), Value::int32(2), Value::int32(3)]
14167 );
14168 }
14169
14170 #[test]
14171 fn queue_microtask_reports_callback_throws_and_continues() {
14172 let program = verified(
14173 vec![
14174 Constant::Int32(7),
14175 Constant::Int32(1),
14176 Constant::String(EcmaString::from_utf8("observed")),
14177 ],
14178 vec![
14179 function(0, 1, vec![Instruction::Halt], Vec::new()),
14180 function(
14181 0,
14182 1,
14183 vec![
14184 Instruction::LoadConst {
14185 dst: reg(0),
14186 constant: cid(0),
14187 },
14188 Instruction::Throw { value: reg(0) },
14189 ],
14190 Vec::new(),
14191 ),
14192 function(
14193 0,
14194 1,
14195 vec![
14196 Instruction::LoadConst {
14197 dst: reg(0),
14198 constant: cid(1),
14199 },
14200 Instruction::StoreGlobal {
14201 name: cid(2),
14202 value: reg(0),
14203 },
14204 Instruction::Return { value: reg(0) },
14205 ],
14206 Vec::new(),
14207 ),
14208 ],
14209 );
14210 let mut host = TestHost;
14211 let mut machine = Machine::new(&program, &mut host, Limits::default());
14212 machine.frames.clear();
14213 machine.live_registers = 0;
14214 let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
14215 machine
14216 .allocate(HeapEntry::Function {
14217 module: ModuleId::new(0),
14218 function: FunctionId::new(function),
14219 captures: Vec::new(),
14220 properties: PropertyMap::default(),
14221 prototype: Some(machine.intrinsics.function_prototype),
14222 extensible: true,
14223 })
14224 .unwrap()
14225 };
14226 let throwing = runtime_function(&mut machine, 1);
14227 let observer = runtime_function(&mut machine, 2);
14228 let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14229 machine
14230 .call_value(queue, Value::UNDEFINED, &[throwing])
14231 .unwrap();
14232 machine
14233 .call_value(queue, Value::UNDEFINED, &[observer])
14234 .unwrap();
14235
14236 let drain = machine.drain_microtasks().unwrap();
14237 assert_eq!(drain.executed, 2);
14238 assert_eq!(
14239 drain.uncaught,
14240 vec![CallbackException {
14241 value: Value::int32(7),
14242 origin: ThrowOrigin::Bytecode,
14243 }]
14244 );
14245 assert_eq!(
14246 machine
14247 .globals
14248 .get(&EcmaString::from_utf8("observed"))
14249 .copied(),
14250 Some(Value::int32(1))
14251 );
14252 }
14253
14254 #[test]
14255 fn microtask_boundaries_preserve_the_queued_head() {
14256 let program = verified(
14257 vec![Constant::Undefined],
14258 vec![
14259 function(0, 1, vec![Instruction::Halt], Vec::new()),
14260 function(
14261 0,
14262 1,
14263 vec![
14264 Instruction::LoadConst {
14265 dst: reg(0),
14266 constant: cid(0),
14267 },
14268 Instruction::Return { value: reg(0) },
14269 ],
14270 Vec::new(),
14271 ),
14272 ],
14273 );
14274 let mut host = TestHost;
14275 let mut machine = Machine::new(
14276 &program,
14277 &mut host,
14278 Limits {
14279 max_microtasks: 1,
14280 ..Limits::default()
14281 },
14282 );
14283 machine.frames.clear();
14284 machine.live_registers = 0;
14285 let callback = machine
14286 .allocate(HeapEntry::Function {
14287 module: ModuleId::new(0),
14288 function: FunctionId::new(1),
14289 captures: Vec::new(),
14290 properties: PropertyMap::default(),
14291 prototype: Some(machine.intrinsics.function_prototype),
14292 extensible: true,
14293 })
14294 .unwrap();
14295 let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14296 assert!(matches!(
14297 machine.call_value(queue, Value::UNDEFINED, &[Value::int32(1)]),
14298 Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
14299 ));
14300 machine
14301 .call_value(queue, Value::UNDEFINED, &[callback])
14302 .unwrap();
14303 assert!(matches!(
14304 machine.call_value(queue, Value::UNDEFINED, &[callback]),
14305 Err(EvalFailure::Runtime(
14306 RuntimeErrorKind::MicrotaskQueueLimitExceeded { limit: 1 }
14307 ))
14308 ));
14309
14310 let fuel = machine.fuel;
14311 machine.microtask_drain_active = true;
14312 let reentry = machine.drain_microtasks().unwrap_err();
14313 assert!(matches!(
14314 reentry.kind,
14315 RuntimeErrorKind::MicrotaskDrainReentry
14316 ));
14317 assert_eq!(machine.fuel, fuel);
14318 assert_eq!(machine.microtasks.len(), 1);
14319 machine.microtask_drain_active = false;
14320
14321 machine.fuel = 0;
14322 let exhausted = machine.drain_microtasks().unwrap_err();
14323 assert!(matches!(
14324 exhausted.kind,
14325 RuntimeErrorKind::FuelExhausted { .. }
14326 ));
14327 assert!(!machine.microtask_drain_active);
14328 assert_eq!(machine.microtasks.len(), 1);
14329
14330 machine.fuel = 100;
14331 let drain = machine.drain_microtasks().unwrap();
14332 assert_eq!(drain.executed, 1);
14333 assert!(machine.microtasks.is_empty());
14334 }
14335
14336 #[derive(Default)]
14339 struct ManualTimerState {
14340 live: std::collections::BTreeMap<u64, u64>,
14341 reports: std::collections::VecDeque<TimerWakeup>,
14342 scheduled: Vec<(u64, u32)>,
14343 cancelled: Vec<u64>,
14344 fail_schedule: bool,
14345 fail_poll: bool,
14346 }
14347
14348 #[derive(Clone, Default)]
14349 struct ManualTimerProvider {
14350 state: std::rc::Rc<std::cell::RefCell<ManualTimerState>>,
14351 }
14352
14353 impl TimerProvider for ManualTimerProvider {
14354 fn schedule(&mut self, id: u64, delay_ms: u32) -> Result<u64, TimerError> {
14355 let mut state = self.state.borrow_mut();
14356 state.scheduled.push((id, delay_ms));
14357 if state.fail_schedule {
14358 return Err(TimerError::new("manual schedule failure"));
14359 }
14360 let deadline = u64::from(delay_ms);
14361 state.live.insert(id, deadline);
14362 Ok(deadline)
14363 }
14364
14365 fn cancel(&mut self, id: u64) -> Result<bool, TimerError> {
14366 let mut state = self.state.borrow_mut();
14367 state.cancelled.push(id);
14368 Ok(state.live.remove(&id).is_some())
14369 }
14370
14371 fn poll_expired(&mut self, output: &mut Vec<TimerWakeup>) -> Result<(), TimerError> {
14372 let mut state = self.state.borrow_mut();
14373 if state.fail_poll {
14374 return Err(TimerError::new("manual poll failure"));
14375 }
14376 output.extend(state.reports.drain(..));
14377 Ok(())
14378 }
14379
14380 fn wait_expired(&mut self) -> Result<Option<TimerWakeup>, TimerError> {
14381 Ok(self.state.borrow_mut().reports.pop_front())
14382 }
14383
14384 fn has_pending(&self) -> bool {
14385 !self.state.borrow().live.is_empty()
14386 }
14387 }
14388
14389 #[derive(Default)]
14390 struct TimerTestHost {
14391 provider: ManualTimerProvider,
14392 }
14393
14394 impl Host for TimerTestHost {
14395 fn timers(&mut self) -> Option<&mut (dyn TimerProvider + 'static)> {
14396 Some(&mut self.provider)
14397 }
14398 }
14399
14400 fn timer_program() -> Program<Verified> {
14401 verified(
14402 vec![
14403 Constant::String(EcmaString::from_utf8("a")),
14404 Constant::String(EcmaString::from_utf8("b")),
14405 Constant::String(EcmaString::from_utf8("this_seen")),
14406 Constant::String(EcmaString::from_utf8("arg_seen")),
14407 Constant::Int32(1),
14408 Constant::Int32(7),
14409 ],
14410 vec![
14411 function(0, 1, vec![Instruction::Halt], Vec::new()),
14412 function(
14413 0,
14414 1,
14415 vec![
14416 Instruction::LoadConst {
14417 dst: reg(0),
14418 constant: cid(4),
14419 },
14420 Instruction::StoreGlobal {
14421 name: cid(0),
14422 value: reg(0),
14423 },
14424 Instruction::Return { value: reg(0) },
14425 ],
14426 Vec::new(),
14427 ),
14428 function(
14429 0,
14430 1,
14431 vec![
14432 Instruction::LoadConst {
14433 dst: reg(0),
14434 constant: cid(4),
14435 },
14436 Instruction::StoreGlobal {
14437 name: cid(1),
14438 value: reg(0),
14439 },
14440 Instruction::Return { value: reg(0) },
14441 ],
14442 Vec::new(),
14443 ),
14444 function(
14445 1,
14446 2,
14447 vec![
14448 Instruction::LoadThis { dst: reg(1) },
14449 Instruction::StoreGlobal {
14450 name: cid(2),
14451 value: reg(1),
14452 },
14453 Instruction::StoreGlobal {
14454 name: cid(3),
14455 value: reg(0),
14456 },
14457 Instruction::Return { value: reg(0) },
14458 ],
14459 Vec::new(),
14460 ),
14461 function(
14462 0,
14463 1,
14464 vec![
14465 Instruction::LoadConst {
14466 dst: reg(0),
14467 constant: cid(5),
14468 },
14469 Instruction::Throw { value: reg(0) },
14470 ],
14471 Vec::new(),
14472 ),
14473 ],
14474 )
14475 }
14476
14477 fn timer_fn(machine: &mut Machine<'_, TimerTestHost>, index: u32) -> Value {
14478 machine
14479 .allocate(HeapEntry::Function {
14480 module: ModuleId::new(0),
14481 function: FunctionId::new(index),
14482 captures: Vec::new(),
14483 properties: PropertyMap::default(),
14484 prototype: Some(machine.intrinsics.function_prototype),
14485 extensible: true,
14486 })
14487 .unwrap()
14488 }
14489
14490 fn read_global(machine: &Machine<'_, TimerTestHost>, name: &str) -> Option<Value> {
14491 machine.globals.get(&EcmaString::from_utf8(name)).copied()
14492 }
14493
14494 fn set_timeout_global(machine: &Machine<'_, TimerTestHost>) -> Value {
14495 machine
14496 .intrinsics
14497 .global("setTimeout")
14498 .expect("setTimeout is installed")
14499 }
14500
14501 fn schedule_nested_timer(
14502 machine: &mut Machine<'_, TimerTestHost>,
14503 _this: Value,
14504 _args: &[Value],
14505 _constructing: bool,
14506 ) -> Result<BuiltinOutcome, EvalFailure> {
14507 let callback = machine
14508 .globals
14509 .get(&EcmaString::from_utf8("nestedCallback"))
14510 .copied()
14511 .expect("test installs nested callback");
14512 let set_timeout = set_timeout_global(machine);
14513 machine.call_value(set_timeout, Value::UNDEFINED, &[callback, Value::int32(1)])?;
14514 Ok(BuiltinOutcome::Value(Value::UNDEFINED))
14515 }
14516
14517 fn timer_native(
14518 machine: &mut Machine<'_, TimerTestHost>,
14519 name: &'static str,
14520 handler: crate::intrinsics::BuiltinHandler<TimerTestHost>,
14521 ) -> Value {
14522 let id = machine
14523 .intrinsics
14524 .builtins
14525 .register(crate::intrinsics::BuiltinDef {
14526 name,
14527 length: 0,
14528 handler,
14529 });
14530 crate::intrinsics::native_function(&mut machine.heap, id, name, 0)
14531 }
14532
14533 #[test]
14534 fn timers_are_absent_without_the_capability() {
14535 let program = timer_program();
14536 let mut host = TestHost;
14537 let mut machine = Machine::new(&program, &mut host, Limits::default());
14538 machine.frames.clear();
14539 machine.live_registers = 0;
14540 assert!(machine.intrinsics.global("setTimeout").is_none());
14541 assert!(machine.intrinsics.global("clearTimeout").is_none());
14542 assert!(!machine.has_pending_timers());
14543 assert_eq!(
14544 machine.run_one_expired_timer().unwrap(),
14545 TimerRun::default()
14546 );
14547 assert!(!machine.wait_for_timer_expiry().unwrap());
14548 }
14549
14550 #[test]
14551 fn set_timeout_rejects_a_non_callable_callback_before_coercion() {
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 failure = machine
14560 .call_value(
14561 set_timeout,
14562 Value::UNDEFINED,
14563 &[Value::int32(3), Value::int32(5)],
14564 )
14565 .unwrap_err();
14566 assert!(matches!(
14567 failure,
14568 EvalFailure::Throw(ThrowOrigin::TypeError { .. })
14569 ));
14570 assert!(shared.borrow().scheduled.is_empty());
14572 assert!(!machine.has_pending_timers());
14573 }
14574
14575 #[test]
14576 fn set_timeout_clamps_and_truncates_like_node() {
14577 let program = timer_program();
14578 let mut host = TimerTestHost::default();
14579 let shared = host.provider.state.clone();
14580 let mut machine = Machine::new(&program, &mut host, Limits::default());
14581 machine.frames.clear();
14582 machine.live_registers = 0;
14583 let set_timeout = set_timeout_global(&machine);
14584 let callback = timer_fn(&mut machine, 1);
14585 for delay in [
14586 Value::int32(0),
14587 Value::number(-5.0),
14588 Value::number(f64::NAN),
14589 Value::number(2_147_483_648.0),
14590 Value::int32(2_147_483_647),
14591 Value::number(3.9),
14592 ] {
14593 machine
14594 .call_value(set_timeout, Value::UNDEFINED, &[callback, delay])
14595 .unwrap();
14596 }
14597 let delays: Vec<u32> = shared.borrow().scheduled.iter().map(|(_, d)| *d).collect();
14598 assert_eq!(delays, vec![1, 1, 1, 1, 2_147_483_647, 3]);
14599 let ids: Vec<u64> = shared
14601 .borrow()
14602 .scheduled
14603 .iter()
14604 .map(|(id, _)| *id)
14605 .collect();
14606 assert_eq!(ids, vec![1, 2, 3, 4, 5, 6]);
14607 }
14608
14609 #[test]
14610 fn same_deadline_timers_run_in_registration_order_despite_reverse_reports() {
14611 let program = timer_program();
14612 let mut host = TimerTestHost::default();
14613 let shared = host.provider.state.clone();
14614 let mut machine = Machine::new(&program, &mut host, Limits::default());
14615 machine.frames.clear();
14616 machine.live_registers = 0;
14617 let set_timeout = set_timeout_global(&machine);
14618 let a = timer_fn(&mut machine, 1);
14619 let b = timer_fn(&mut machine, 2);
14620 machine
14621 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(5)])
14622 .unwrap();
14623 machine
14624 .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(5)])
14625 .unwrap();
14626 shared.borrow_mut().reports.push_back(TimerWakeup {
14628 id: 2,
14629 deadline_ms: 5,
14630 });
14631 let first = machine.run_one_expired_timer().unwrap();
14632 assert_eq!(first.executed, 1);
14633 assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14634 assert_eq!(read_global(&machine, "b"), None);
14635 let second = machine.run_one_expired_timer().unwrap();
14636 assert_eq!(second.executed, 1);
14637 assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14638 assert!(!machine.has_pending_timers());
14639 }
14640
14641 #[test]
14642 fn a_shorter_deadline_beats_an_older_sequence() {
14643 let program = timer_program();
14644 let mut host = TimerTestHost::default();
14645 let shared = host.provider.state.clone();
14646 let mut machine = Machine::new(&program, &mut host, Limits::default());
14647 machine.frames.clear();
14648 machine.live_registers = 0;
14649 let set_timeout = set_timeout_global(&machine);
14650 let a = timer_fn(&mut machine, 1);
14651 let b = timer_fn(&mut machine, 2);
14652 machine
14653 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(5)])
14654 .unwrap();
14655 machine
14656 .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(3)])
14657 .unwrap();
14658 shared.borrow_mut().reports.push_back(TimerWakeup {
14659 id: 1,
14660 deadline_ms: 5,
14661 });
14662 machine.run_one_expired_timer().unwrap();
14663 assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14664 assert_eq!(read_global(&machine, "a"), None);
14665 }
14666
14667 #[test]
14668 fn clear_timeout_prevents_a_ready_timer_and_ignores_stale_ids() {
14669 let program = timer_program();
14670 let mut host = TimerTestHost::default();
14671 let shared = host.provider.state.clone();
14672 let mut machine = Machine::new(&program, &mut host, Limits::default());
14673 machine.frames.clear();
14674 machine.live_registers = 0;
14675 let set_timeout = set_timeout_global(&machine);
14676 let clear_timeout = machine.intrinsics.global("clearTimeout").unwrap();
14677 let a = timer_fn(&mut machine, 1);
14678 let b = timer_fn(&mut machine, 2);
14679 let handle_a = machine
14680 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14681 .unwrap();
14682 machine
14683 .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(3)])
14684 .unwrap();
14685 shared.borrow_mut().reports.push_back(TimerWakeup {
14687 id: 1,
14688 deadline_ms: 3,
14689 });
14690 machine
14691 .call_value(clear_timeout, Value::UNDEFINED, &[handle_a])
14692 .unwrap();
14693 assert!(shared.borrow().cancelled.contains(&1));
14694 machine
14696 .call_value(clear_timeout, Value::UNDEFINED, &[Value::int32(1)])
14697 .unwrap();
14698 shared.borrow_mut().reports.push_back(TimerWakeup {
14699 id: 2,
14700 deadline_ms: 3,
14701 });
14702 let run = machine.run_one_expired_timer().unwrap();
14703 assert_eq!(run.executed, 1);
14704 assert_eq!(read_global(&machine, "a"), None);
14705 assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14706 }
14707
14708 #[test]
14709 fn clear_timeout_accepts_a_direct_positive_integer_id() {
14710 let program = timer_program();
14711 let mut host = TimerTestHost::default();
14712 let shared = host.provider.state.clone();
14713 let mut machine = Machine::new(&program, &mut host, Limits::default());
14714 machine.frames.clear();
14715 machine.live_registers = 0;
14716 let set_timeout = set_timeout_global(&machine);
14717 let clear_timeout = machine.intrinsics.global("clearTimeout").unwrap();
14718 let a = timer_fn(&mut machine, 1);
14719 machine
14720 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14721 .unwrap();
14722 machine
14723 .call_value(clear_timeout, Value::UNDEFINED, &[Value::int32(1)])
14724 .unwrap();
14725 assert!(!machine.has_pending_timers());
14726 shared.borrow_mut().reports.push_back(TimerWakeup {
14727 id: 1,
14728 deadline_ms: 3,
14729 });
14730 assert_eq!(machine.run_one_expired_timer().unwrap().executed, 0);
14731
14732 machine.next_timer_id = Some(u64::MAX);
14733 let handle = machine
14734 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14735 .unwrap();
14736 machine
14737 .call_value(
14738 clear_timeout,
14739 Value::UNDEFINED,
14740 &[Value::number(u64::MAX as f64)],
14741 )
14742 .unwrap();
14743 assert!(machine.has_pending_timers());
14744 machine
14745 .call_value(clear_timeout, Value::UNDEFINED, &[handle])
14746 .unwrap();
14747 assert!(!machine.has_pending_timers());
14748 machine
14750 .call_value(clear_timeout, Value::UNDEFINED, &[Value::UNDEFINED])
14751 .unwrap();
14752 }
14753
14754 #[test]
14755 fn timer_callback_receives_trailing_args_and_the_handle_as_this() {
14756 let program = timer_program();
14757 let mut host = TimerTestHost::default();
14758 let shared = host.provider.state.clone();
14759 let mut machine = Machine::new(&program, &mut host, Limits::default());
14760 machine.frames.clear();
14761 machine.live_registers = 0;
14762 let set_timeout = set_timeout_global(&machine);
14763 let callback = timer_fn(&mut machine, 3);
14764 let handle = machine
14765 .call_value(
14766 set_timeout,
14767 Value::UNDEFINED,
14768 &[callback, Value::int32(1), Value::int32(42)],
14769 )
14770 .unwrap();
14771 shared.borrow_mut().reports.push_back(TimerWakeup {
14772 id: 1,
14773 deadline_ms: 1,
14774 });
14775 machine.run_one_expired_timer().unwrap();
14776 assert_eq!(read_global(&machine, "this_seen"), Some(handle));
14777 assert_eq!(read_global(&machine, "arg_seen"), Some(Value::int32(42)));
14778 }
14779
14780 #[test]
14781 fn a_callback_created_timer_waits_for_a_later_checkpoint() {
14782 let program = timer_program();
14783 let mut host = TimerTestHost::default();
14784 let shared = host.provider.state.clone();
14785 let mut machine = Machine::new(&program, &mut host, Limits::default());
14786 machine.frames.clear();
14787 machine.live_registers = 0;
14788 let set_timeout = set_timeout_global(&machine);
14789 let nested = timer_fn(&mut machine, 2);
14790 machine
14791 .globals
14792 .insert(EcmaString::from_utf8("nestedCallback"), nested);
14793 let creator = timer_native(&mut machine, "schedule nested", schedule_nested_timer);
14794 machine
14795 .call_value(set_timeout, Value::UNDEFINED, &[creator, Value::int32(1)])
14796 .unwrap();
14797 shared.borrow_mut().reports.push_back(TimerWakeup {
14798 id: 1,
14799 deadline_ms: 1,
14800 });
14801 assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14802 assert_eq!(read_global(&machine, "b"), None);
14803 assert!(machine.has_pending_timers());
14804 shared.borrow_mut().reports.push_back(TimerWakeup {
14807 id: 2,
14808 deadline_ms: 1,
14809 });
14810 assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14811 assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14812 }
14813
14814 #[test]
14815 fn timer_callback_throw_is_reported_and_a_runtime_failure_propagates() {
14816 let program = timer_program();
14817 let mut host = TimerTestHost::default();
14818 let shared = host.provider.state.clone();
14819 let mut machine = Machine::new(&program, &mut host, Limits::default());
14820 machine.frames.clear();
14821 machine.live_registers = 0;
14822 let set_timeout = set_timeout_global(&machine);
14823 let thrower = timer_fn(&mut machine, 4);
14824 machine
14825 .call_value(set_timeout, Value::UNDEFINED, &[thrower, Value::int32(1)])
14826 .unwrap();
14827 shared.borrow_mut().reports.push_back(TimerWakeup {
14828 id: 1,
14829 deadline_ms: 1,
14830 });
14831 let run = machine.run_one_expired_timer().unwrap();
14832 assert_eq!(run.executed, 1);
14833 assert_eq!(
14834 run.uncaught,
14835 vec![CallbackException {
14836 value: Value::int32(7),
14837 origin: ThrowOrigin::Bytecode
14838 }]
14839 );
14840
14841 let another = timer_fn(&mut machine, 1);
14843 machine
14844 .call_value(set_timeout, Value::UNDEFINED, &[another, Value::int32(1)])
14845 .unwrap();
14846 shared.borrow_mut().reports.push_back(TimerWakeup {
14847 id: 2,
14848 deadline_ms: 1,
14849 });
14850 machine.fuel = 1;
14851 let error = machine.run_one_expired_timer().unwrap_err();
14852 assert!(matches!(error.kind, RuntimeErrorKind::FuelExhausted { .. }));
14853 }
14854
14855 #[test]
14856 fn a_timer_checkpoint_never_drains_microtasks() {
14857 let program = timer_program();
14858 let mut host = TimerTestHost::default();
14859 let shared = host.provider.state.clone();
14860 let mut machine = Machine::new(&program, &mut host, Limits::default());
14861 machine.frames.clear();
14862 machine.live_registers = 0;
14863 let set_timeout = set_timeout_global(&machine);
14864 let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14865 let a = timer_fn(&mut machine, 1);
14866 let b = timer_fn(&mut machine, 2);
14867 machine
14868 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14869 .unwrap();
14870 machine.call_value(queue, Value::UNDEFINED, &[b]).unwrap();
14871 shared.borrow_mut().reports.push_back(TimerWakeup {
14872 id: 1,
14873 deadline_ms: 1,
14874 });
14875 let run = machine.run_one_expired_timer().unwrap();
14876 assert_eq!(run.executed, 1);
14877 assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14878 assert_eq!(read_global(&machine, "b"), None);
14879 assert_eq!(machine.microtasks.len(), 1);
14880 machine.drain_microtasks().unwrap();
14881 assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14882 }
14883
14884 #[test]
14885 fn timer_reentry_capacity_and_fuel_preserve_state() {
14886 let program = timer_program();
14887 let mut host = TimerTestHost::default();
14888 let shared = host.provider.state.clone();
14889 let mut machine = Machine::new(
14890 &program,
14891 &mut host,
14892 Limits {
14893 max_timers: 1,
14894 ..Limits::default()
14895 },
14896 );
14897 machine.frames.clear();
14898 machine.live_registers = 0;
14899 let set_timeout = set_timeout_global(&machine);
14900 let a = timer_fn(&mut machine, 1);
14901 let b = timer_fn(&mut machine, 2);
14902 machine
14903 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14904 .unwrap();
14905 let capacity = machine
14907 .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(1)])
14908 .unwrap_err();
14909 assert!(matches!(
14910 capacity,
14911 EvalFailure::Runtime(RuntimeErrorKind::TimerCapacityExceeded { limit: 1 })
14912 ));
14913 assert_eq!(shared.borrow().scheduled.len(), 1);
14914
14915 shared.borrow_mut().reports.push_back(TimerWakeup {
14917 id: 1,
14918 deadline_ms: 1,
14919 });
14920 machine.timer_checkpoint_active = true;
14921 let fuel = machine.fuel;
14922 let reentry = machine.run_one_expired_timer().unwrap_err();
14923 assert!(matches!(
14924 reentry.kind,
14925 RuntimeErrorKind::TimerCheckpointReentry
14926 ));
14927 assert_eq!(machine.fuel, fuel);
14928 machine.timer_checkpoint_active = false;
14929
14930 machine.fuel = 0;
14932 let exhausted = machine.run_one_expired_timer().unwrap_err();
14933 assert!(matches!(
14934 exhausted.kind,
14935 RuntimeErrorKind::FuelExhausted { .. }
14936 ));
14937 assert!(machine.has_pending_timers());
14938 machine.fuel = 100;
14939 assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14940 assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14941 }
14942
14943 #[test]
14944 fn a_failed_schedule_never_reuses_its_timer_id() {
14945 let program = timer_program();
14946 let mut host = TimerTestHost::default();
14947 let shared = host.provider.state.clone();
14948 let mut machine = Machine::new(&program, &mut host, Limits::default());
14949 machine.frames.clear();
14950 machine.live_registers = 0;
14951 let set_timeout = set_timeout_global(&machine);
14952 let a = timer_fn(&mut machine, 1);
14953 shared.borrow_mut().fail_schedule = true;
14954 let failure = machine
14955 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14956 .unwrap_err();
14957 assert!(matches!(
14958 failure,
14959 EvalFailure::Runtime(RuntimeErrorKind::TimerProviderFailure { .. })
14960 ));
14961 shared.borrow_mut().fail_schedule = false;
14962 machine
14963 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14964 .unwrap();
14965 let ids: Vec<u64> = shared
14966 .borrow()
14967 .scheduled
14968 .iter()
14969 .map(|(id, _)| *id)
14970 .collect();
14971 assert_eq!(ids, vec![1, 2]);
14972 }
14973
14974 #[test]
14975 fn wait_for_timer_expiry_promotes_a_reported_timer() {
14976 let program = timer_program();
14977 let mut host = TimerTestHost::default();
14978 let shared = host.provider.state.clone();
14979 let mut machine = Machine::new(&program, &mut host, Limits::default());
14980 machine.frames.clear();
14981 machine.live_registers = 0;
14982 assert!(!machine.wait_for_timer_expiry().unwrap());
14983 let set_timeout = set_timeout_global(&machine);
14984 let a = timer_fn(&mut machine, 1);
14985 machine
14986 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14987 .unwrap();
14988 shared.borrow_mut().reports.push_back(TimerWakeup {
14989 id: 1,
14990 deadline_ms: 1,
14991 });
14992 assert!(machine.wait_for_timer_expiry().unwrap());
14993 assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14994 assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14995 }
14996
14997 fn loop_test_program() -> Program<Verified> {
15004 verified(
15005 vec![
15006 Constant::String(EcmaString::from_utf8("order")), Constant::Int32(1), Constant::Int32(2), Constant::Int32(3), Constant::Int32(4), Constant::String(EcmaString::from_utf8("queueMicrotask")), Constant::String(EcmaString::from_utf8("job")), Constant::Undefined, ],
15015 vec![
15016 function(
15017 0,
15018 7,
15019 vec![
15020 Instruction::LoadGlobal {
15021 dst: reg(0),
15022 name: cid(5),
15023 },
15024 Instruction::LoadGlobal {
15025 dst: reg(1),
15026 name: cid(6),
15027 },
15028 Instruction::CreateArray { dst: reg(2) },
15029 Instruction::ArrayPush {
15030 array: reg(2),
15031 value: reg(1),
15032 },
15033 Instruction::LoadConst {
15034 dst: reg(3),
15035 constant: cid(7),
15036 },
15037 Instruction::Call {
15038 dst: reg(4),
15039 callee: reg(0),
15040 this_value: reg(3),
15041 arguments: reg(2),
15042 },
15043 Instruction::Return { value: reg(3) },
15044 ],
15045 Vec::new(),
15046 ),
15047 function(
15048 0,
15049 7,
15050 vec![
15051 Instruction::LoadGlobal {
15052 dst: reg(0),
15053 name: cid(0),
15054 },
15055 Instruction::LoadConst {
15056 dst: reg(1),
15057 constant: cid(1),
15058 },
15059 Instruction::ArrayPush {
15060 array: reg(0),
15061 value: reg(1),
15062 },
15063 Instruction::LoadGlobal {
15064 dst: reg(2),
15065 name: cid(5),
15066 },
15067 Instruction::LoadGlobal {
15068 dst: reg(3),
15069 name: cid(6),
15070 },
15071 Instruction::CreateArray { dst: reg(4) },
15072 Instruction::ArrayPush {
15073 array: reg(4),
15074 value: reg(3),
15075 },
15076 Instruction::LoadConst {
15077 dst: reg(5),
15078 constant: cid(7),
15079 },
15080 Instruction::Call {
15081 dst: reg(6),
15082 callee: reg(2),
15083 this_value: reg(5),
15084 arguments: reg(4),
15085 },
15086 Instruction::Return { value: reg(1) },
15087 ],
15088 Vec::new(),
15089 ),
15090 function(
15091 0,
15092 2,
15093 vec![
15094 Instruction::LoadGlobal {
15095 dst: reg(0),
15096 name: cid(0),
15097 },
15098 Instruction::LoadConst {
15099 dst: reg(1),
15100 constant: cid(2),
15101 },
15102 Instruction::ArrayPush {
15103 array: reg(0),
15104 value: reg(1),
15105 },
15106 Instruction::Return { value: reg(1) },
15107 ],
15108 Vec::new(),
15109 ),
15110 function(
15111 0,
15112 2,
15113 vec![
15114 Instruction::LoadGlobal {
15115 dst: reg(0),
15116 name: cid(0),
15117 },
15118 Instruction::LoadConst {
15119 dst: reg(1),
15120 constant: cid(3),
15121 },
15122 Instruction::ArrayPush {
15123 array: reg(0),
15124 value: reg(1),
15125 },
15126 Instruction::Return { value: reg(1) },
15127 ],
15128 Vec::new(),
15129 ),
15130 function(
15131 0,
15132 2,
15133 vec![
15134 Instruction::LoadGlobal {
15135 dst: reg(0),
15136 name: cid(0),
15137 },
15138 Instruction::LoadConst {
15139 dst: reg(1),
15140 constant: cid(4),
15141 },
15142 Instruction::ArrayPush {
15143 array: reg(0),
15144 value: reg(1),
15145 },
15146 Instruction::Return { value: reg(1) },
15147 ],
15148 Vec::new(),
15149 ),
15150 ],
15151 )
15152 }
15153
15154 fn promise_throw_program() -> Program<Verified> {
15155 verified(
15156 vec![
15157 Constant::String(EcmaString::from_utf8("resolve")), Constant::String(EcmaString::from_utf8("reject")), Constant::String(EcmaString::from_utf8("observed")), Constant::Int32(7), ],
15162 vec![
15163 function(0, 1, vec![Instruction::Halt], Vec::new()),
15164 function(
15165 2,
15166 2,
15167 vec![
15168 Instruction::StoreGlobal {
15169 name: cid(0),
15170 value: reg(0),
15171 },
15172 Instruction::StoreGlobal {
15173 name: cid(1),
15174 value: reg(1),
15175 },
15176 Instruction::Return { value: reg(0) },
15177 ],
15178 Vec::new(),
15179 ),
15180 function(
15181 1,
15182 1,
15183 vec![
15184 Instruction::LoadConst {
15185 dst: reg(0),
15186 constant: cid(3),
15187 },
15188 Instruction::Throw { value: reg(0) },
15189 ],
15190 Vec::new(),
15191 ),
15192 function(
15193 1,
15194 1,
15195 vec![
15196 Instruction::StoreGlobal {
15197 name: cid(2),
15198 value: reg(0),
15199 },
15200 Instruction::Return { value: reg(0) },
15201 ],
15202 Vec::new(),
15203 ),
15204 ],
15205 )
15206 }
15207
15208 fn install_order_array(machine: &mut Machine<'_, TimerTestHost>) -> Value {
15209 let order = machine
15210 .allocate(HeapEntry::Array {
15211 elements: Vec::new(),
15212 properties: PropertyMap::default(),
15213 prototype: Some(machine.intrinsics.array_prototype),
15214 extensible: true,
15215 length_writable: true,
15216 })
15217 .unwrap();
15218 machine
15219 .globals
15220 .insert(EcmaString::from_utf8("order"), order);
15221 order
15222 }
15223
15224 fn order_markers(machine: &Machine<'_, TimerTestHost>) -> Vec<Value> {
15225 let order = machine
15226 .globals
15227 .get(&EcmaString::from_utf8("order"))
15228 .copied()
15229 .expect("order array is installed");
15230 let index = machine
15231 .runtime_slot(order)
15232 .expect("order resolves")
15233 .expect("order is a heap value");
15234 let HeapEntry::Array { elements, .. } = &machine.heap[index] else {
15235 panic!("order remains an array");
15236 };
15237 elements.clone()
15238 }
15239
15240 fn schedule_global_job(
15241 machine: &mut Machine<'_, TimerTestHost>,
15242 global: &str,
15243 ) -> Result<BuiltinOutcome, EvalFailure> {
15244 let job = machine
15245 .globals
15246 .get(&EcmaString::from_utf8(global))
15247 .copied()
15248 .unwrap_or_else(|| panic!("test installs the {global} job"));
15249 let queue = machine
15250 .intrinsics
15251 .global("queueMicrotask")
15252 .expect("queueMicrotask is installed");
15253 machine.call_value(queue, Value::UNDEFINED, &[job])?;
15254 Ok(BuiltinOutcome::Value(Value::UNDEFINED))
15255 }
15256
15257 fn queue_job_then_throw(
15258 machine: &mut Machine<'_, TimerTestHost>,
15259 _this: Value,
15260 _args: &[Value],
15261 _constructing: bool,
15262 ) -> Result<BuiltinOutcome, EvalFailure> {
15263 schedule_global_job(machine, "nestedCallback")?;
15264 Err(EvalFailure::ThrowValue(Value::int32(7)))
15265 }
15266
15267 fn respawn_job(
15268 machine: &mut Machine<'_, TimerTestHost>,
15269 _this: Value,
15270 _args: &[Value],
15271 _constructing: bool,
15272 ) -> Result<BuiltinOutcome, EvalFailure> {
15273 schedule_global_job(machine, "nestedCallback")
15274 }
15275
15276
15277 #[test]
15278 fn automatic_loop_leaves_an_idle_machine_untouched() {
15279 let program = timer_program();
15280 let mut host = TimerTestHost::default();
15281 let mut machine = Machine::new(&program, &mut host, Limits::default());
15282 machine.frames.clear();
15283 machine.live_registers = 0;
15284 let fuel = machine.fuel;
15285 machine.run_to_quiescence().unwrap();
15286 assert_eq!(machine.fuel, fuel);
15287 assert!(machine.microtasks.is_empty());
15288 assert!(!machine.has_pending_timers());
15289 assert!(!machine.microtask_drain_active);
15290 assert!(!machine.timer_checkpoint_active);
15291 }
15292
15293 #[test]
15294 fn run_returns_the_synchronous_execution_snapshot() {
15295 let program = loop_test_program();
15296 let mut host = TimerTestHost::default();
15297 let mut first = Machine::new(&program, &mut host, Limits::default());
15298 first.frames.clear();
15299 first.live_registers = 0;
15300 install_order_array(&mut first);
15301 first
15302 .globals
15303 .insert(EcmaString::from_utf8("job"), timer_fn(&mut first, 2));
15304 let snapshot = first.evaluate().unwrap();
15305 assert!(order_markers(&first).is_empty());
15306 first.run_to_quiescence().unwrap();
15307 assert_eq!(order_markers(&first), vec![Value::int32(2)]);
15308 drop(first);
15309
15310 let mut host = TimerTestHost::default();
15311 let mut second = Machine::new(&program, &mut host, Limits::default());
15312 second.frames.clear();
15313 second.live_registers = 0;
15314 install_order_array(&mut second);
15315 second
15316 .globals
15317 .insert(EcmaString::from_utf8("job"), timer_fn(&mut second, 2));
15318 let execution = second.run().unwrap();
15319 assert_eq!(execution, snapshot);
15320 }
15321
15322 #[test]
15323 fn automatic_loop_drains_nested_microtasks_in_fifo_order() {
15324 let program = loop_test_program();
15325 let mut host = TimerTestHost::default();
15326 let mut machine = Machine::new(&program, &mut host, Limits::default());
15327 machine.frames.clear();
15328 machine.live_registers = 0;
15329 install_order_array(&mut machine);
15330 let first = timer_fn(&mut machine, 1); let second = timer_fn(&mut machine, 2); let third = timer_fn(&mut machine, 3); machine
15334 .globals
15335 .insert(EcmaString::from_utf8("job"), third);
15336 let queue = machine.intrinsics.global("queueMicrotask").unwrap();
15337 machine
15338 .call_value(queue, Value::UNDEFINED, &[first])
15339 .unwrap();
15340 machine
15341 .call_value(queue, Value::UNDEFINED, &[second])
15342 .unwrap();
15343
15344 machine.run_to_quiescence().unwrap();
15345 assert_eq!(
15346 order_markers(&machine),
15347 vec![Value::int32(1), Value::int32(2), Value::int32(3)]
15348 );
15349 assert!(machine.microtasks.is_empty());
15350 }
15351
15352 #[test]
15353 fn automatic_loop_runs_two_timers_with_a_full_drain_between_turns() {
15354 let program = loop_test_program();
15355 let mut host = TimerTestHost::default();
15356 let shared = host.provider.state.clone();
15357 let mut machine = Machine::new(&program, &mut host, Limits::default());
15358 machine.frames.clear();
15359 machine.live_registers = 0;
15360 install_order_array(&mut machine);
15361 let first = timer_fn(&mut machine, 1); let second = timer_fn(&mut machine, 3); let microtask = timer_fn(&mut machine, 2); machine
15365 .globals
15366 .insert(EcmaString::from_utf8("job"), microtask);
15367 let set_timeout = set_timeout_global(&machine);
15368 machine
15369 .call_value(set_timeout, Value::UNDEFINED, &[first, Value::int32(5)])
15370 .unwrap();
15371 machine
15372 .call_value(set_timeout, Value::UNDEFINED, &[second, Value::int32(5)])
15373 .unwrap();
15374 shared.borrow_mut().reports.push_back(TimerWakeup {
15375 id: 1,
15376 deadline_ms: 5,
15377 });
15378 shared.borrow_mut().reports.push_back(TimerWakeup {
15379 id: 2,
15380 deadline_ms: 5,
15381 });
15382
15383 machine.run_to_quiescence().unwrap();
15384 assert_eq!(
15386 order_markers(&machine),
15387 vec![Value::int32(1), Value::int32(2), Value::int32(3)]
15388 );
15389 assert!(machine.microtasks.is_empty());
15390 assert!(!machine.has_pending_timers());
15391 }
15392
15393 #[test]
15394 fn automatic_loop_runs_a_timer_created_timer_in_a_later_turn() {
15395 let program = timer_program();
15396 let mut host = TimerTestHost::default();
15397 let shared = host.provider.state.clone();
15398 let mut machine = Machine::new(&program, &mut host, Limits::default());
15399 machine.frames.clear();
15400 machine.live_registers = 0;
15401 let nested = timer_fn(&mut machine, 2);
15402 machine
15403 .globals
15404 .insert(EcmaString::from_utf8("nestedCallback"), nested);
15405 let creator = timer_native(&mut machine, "schedule nested", schedule_nested_timer);
15406 let set_timeout = set_timeout_global(&machine);
15407 machine
15408 .call_value(set_timeout, Value::UNDEFINED, &[creator, Value::int32(1)])
15409 .unwrap();
15410 shared.borrow_mut().reports.push_back(TimerWakeup {
15411 id: 1,
15412 deadline_ms: 1,
15413 });
15414 shared.borrow_mut().reports.push_back(TimerWakeup {
15415 id: 2,
15416 deadline_ms: 1,
15417 });
15418
15419 machine.run_to_quiescence().unwrap();
15420 assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
15421 assert!(!machine.has_pending_timers());
15422 assert!(machine.microtasks.is_empty());
15423 }
15424
15425 #[test]
15426 fn automatic_loop_ignores_stale_and_premature_reports_until_real_expiry() {
15427 let program = timer_program();
15428 let mut host = TimerTestHost::default();
15429 let shared = host.provider.state.clone();
15430 let mut machine = Machine::new(&program, &mut host, Limits::default());
15431 machine.frames.clear();
15432 machine.live_registers = 0;
15433 let a = timer_fn(&mut machine, 1);
15434 let set_timeout = set_timeout_global(&machine);
15435 machine
15436 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(50)])
15437 .unwrap();
15438 shared.borrow_mut().reports.push_back(TimerWakeup {
15440 id: 999,
15441 deadline_ms: 10,
15442 });
15443 shared.borrow_mut().reports.push_back(TimerWakeup {
15445 id: 1,
15446 deadline_ms: 10,
15447 });
15448 shared.borrow_mut().reports.push_back(TimerWakeup {
15450 id: 1,
15451 deadline_ms: 50,
15452 });
15453
15454 machine.run_to_quiescence().unwrap();
15455 assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
15456 assert!(!machine.has_pending_timers());
15457 }
15458
15459 #[test]
15460 fn automatic_loop_fails_with_typed_error_when_provider_loses_a_live_timer() {
15461 let program = timer_program();
15462 let mut host = TimerTestHost::default();
15463 let shared = host.provider.state.clone();
15464 let mut machine = Machine::new(&program, &mut host, Limits::default());
15465 machine.frames.clear();
15466 machine.live_registers = 0;
15467 let a = timer_fn(&mut machine, 1);
15468 let set_timeout = set_timeout_global(&machine);
15469 machine
15470 .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(5)])
15471 .unwrap();
15472 shared.borrow_mut().live.remove(&1);
15474
15475 let error = machine.run_to_quiescence().unwrap_err();
15476 assert!(matches!(
15477 error.kind,
15478 RuntimeErrorKind::TimerProviderFailure { .. }
15479 ));
15480 assert!(machine.has_pending_timers());
15482 assert!(!machine.microtask_drain_active);
15483 assert!(!machine.timer_checkpoint_active);
15484 }
15485
15486 #[test]
15487 fn automatic_loop_maps_the_first_uncaught_microtask_throw_and_stops() {
15488 let program = timer_program();
15489 let mut host = TimerTestHost::default();
15490 let mut machine = Machine::new(&program, &mut host, Limits::default());
15491 machine.frames.clear();
15492 machine.live_registers = 0;
15493 let throwing = timer_fn(&mut machine, 4); let observer = timer_fn(&mut machine, 1); let queue = machine.intrinsics.global("queueMicrotask").unwrap();
15496 machine
15497 .call_value(queue, Value::UNDEFINED, &[throwing])
15498 .unwrap();
15499 machine
15500 .call_value(queue, Value::UNDEFINED, &[observer])
15501 .unwrap();
15502
15503 let error = machine.run_to_quiescence().unwrap_err();
15504 assert_eq!(
15505 error.kind,
15506 RuntimeErrorKind::UncaughtThrow {
15507 value: Value::int32(7),
15508 origin: ThrowOrigin::Bytecode,
15509 }
15510 );
15511 assert_eq!(read_global(&machine, "a"), None);
15513 assert_eq!(machine.microtasks.len(), 1);
15514 assert!(!machine.microtask_drain_active);
15515 }
15516
15517 #[test]
15518 fn automatic_loop_timer_throw_suppresses_queued_microtasks_and_later_timers() {
15519 let program = timer_program();
15520 let mut host = TimerTestHost::default();
15521 let shared = host.provider.state.clone();
15522 let mut machine = Machine::new(&program, &mut host, Limits::default());
15523 machine.frames.clear();
15524 machine.live_registers = 0;
15525 let suppressed_microtask = timer_fn(&mut machine, 2); machine
15527 .globals
15528 .insert(EcmaString::from_utf8("nestedCallback"), suppressed_microtask);
15529 let thrower = timer_native(&mut machine, "queue then throw", queue_job_then_throw);
15530 let later_timer = timer_fn(&mut machine, 1); let set_timeout = set_timeout_global(&machine);
15532 machine
15533 .call_value(set_timeout, Value::UNDEFINED, &[thrower, Value::int32(1)])
15534 .unwrap();
15535 machine
15536 .call_value(set_timeout, Value::UNDEFINED, &[later_timer, Value::int32(2)])
15537 .unwrap();
15538 shared.borrow_mut().reports.push_back(TimerWakeup {
15539 id: 1,
15540 deadline_ms: 1,
15541 });
15542 shared.borrow_mut().reports.push_back(TimerWakeup {
15543 id: 2,
15544 deadline_ms: 2,
15545 });
15546
15547 let error = machine.run_to_quiescence().unwrap_err();
15548 assert_eq!(
15549 error.kind,
15550 RuntimeErrorKind::UncaughtThrow {
15551 value: Value::int32(7),
15552 origin: ThrowOrigin::Bytecode,
15553 }
15554 );
15555 assert_eq!(read_global(&machine, "b"), None);
15558 assert_eq!(read_global(&machine, "a"), None);
15559 assert_eq!(machine.microtasks.len(), 1);
15560 assert!(machine.has_pending_timers());
15561 assert!(!machine.microtask_drain_active);
15562 assert!(!machine.timer_checkpoint_active);
15563 }
15564
15565 #[test]
15566 fn automatic_loop_settles_derived_promises_when_a_handler_throws() {
15567 let program = promise_throw_program();
15568 let mut host = TimerTestHost::default();
15569 let mut machine = Machine::new(&program, &mut host, Limits::default());
15570 machine.frames.clear();
15571 machine.live_registers = 0;
15572 let executor = timer_fn(&mut machine, 1);
15573 let throwing = timer_fn(&mut machine, 2);
15574 let observer = timer_fn(&mut machine, 3);
15575 let constructor = machine.intrinsics.global("Promise").unwrap();
15576 let constructor_index = machine.runtime_slot(constructor).unwrap().unwrap();
15577 let HeapEntry::NativeFunction {
15578 callable: NativeCallable::Builtin(constructor_id),
15579 ..
15580 } = machine.heap[constructor_index]
15581 else {
15582 panic!("Promise must be a native constructor");
15583 };
15584 let BuiltinOutcome::Value(promise) = machine
15585 .call_builtin(constructor_id, Value::UNDEFINED, &[executor], true)
15586 .unwrap()
15587 else {
15588 panic!("Promise construction returns a Promise");
15589 };
15590 let resolve = machine
15591 .globals
15592 .get(&EcmaString::from_utf8("resolve"))
15593 .copied()
15594 .unwrap();
15595 machine
15596 .call_value(resolve, Value::UNDEFINED, &[Value::int32(1)])
15597 .unwrap();
15598 let then = machine.get_named_property(promise, "then").unwrap();
15599 let derived = machine.call_value(then, promise, &[throwing]).unwrap();
15600 let then_again = machine.get_named_property(derived, "then").unwrap();
15601 machine
15602 .call_value(then_again, derived, &[Value::UNDEFINED, observer])
15603 .unwrap();
15604
15605 machine.run_to_quiescence().unwrap();
15606 assert_eq!(
15609 machine
15610 .globals
15611 .get(&EcmaString::from_utf8("observed"))
15612 .copied(),
15613 Some(Value::int32(7))
15614 );
15615 assert!(machine.microtasks.is_empty());
15616 }
15617
15618 #[test]
15619 fn recursive_microtask_work_reaches_existing_fuel() {
15620 let program = timer_program();
15621 let mut host = TimerTestHost::default();
15622 let mut machine = Machine::new(&program, &mut host, Limits::default());
15623 machine.frames.clear();
15624 machine.live_registers = 0;
15625 let respawn = timer_native(&mut machine, "respawn", respawn_job);
15626 machine
15627 .globals
15628 .insert(EcmaString::from_utf8("nestedCallback"), respawn);
15629 let queue = machine.intrinsics.global("queueMicrotask").unwrap();
15630 machine
15631 .call_value(queue, Value::UNDEFINED, &[respawn])
15632 .unwrap();
15633 machine.fuel = 16;
15634
15635 let error = machine.run_to_quiescence().unwrap_err();
15636 assert!(matches!(
15637 error.kind,
15638 RuntimeErrorKind::FuelExhausted { .. }
15639 ));
15640 assert_eq!(machine.fuel, 0);
15642 assert!(!machine.microtask_drain_active);
15643 }
15644
15645 #[test]
15646 fn manual_drain_still_collects_every_throw_and_continues() {
15647 let program = timer_program();
15648 let mut host = TimerTestHost::default();
15649 let mut machine = Machine::new(&program, &mut host, Limits::default());
15650 machine.frames.clear();
15651 machine.live_registers = 0;
15652 let throwing = timer_fn(&mut machine, 4); let observer = timer_fn(&mut machine, 1); let queue = machine.intrinsics.global("queueMicrotask").unwrap();
15655 machine
15656 .call_value(queue, Value::UNDEFINED, &[throwing])
15657 .unwrap();
15658 machine
15659 .call_value(queue, Value::UNDEFINED, &[observer])
15660 .unwrap();
15661 machine
15662 .call_value(queue, Value::UNDEFINED, &[throwing])
15663 .unwrap();
15664
15665 let drain = machine.drain_microtasks().unwrap();
15666 assert_eq!(drain.executed, 3);
15667 assert_eq!(
15668 drain.uncaught,
15669 vec![
15670 CallbackException {
15671 value: Value::int32(7),
15672 origin: ThrowOrigin::Bytecode,
15673 },
15674 CallbackException {
15675 value: Value::int32(7),
15676 origin: ThrowOrigin::Bytecode,
15677 },
15678 ]
15679 );
15680 assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
15681 assert!(machine.microtasks.is_empty());
15682 }
15683}