1use core::num::{NonZeroU16, NonZeroU32};
25
26const HEADER_SHIFT: u32 = 51;
30
31const TAG_SHIFT: u32 = 48;
33
34const TAG_MASK: u64 = 0b111;
36
37const PAYLOAD_MASK: u64 = (1u64 << 48) - 1;
39
40const UPPER_MASK: u64 = 0xFFFF_0000_0000;
42
43const HEADER_MASK: u64 = 0x1FFFu64 << HEADER_SHIFT;
45
46const CANON_NAN: u64 = 4095u64 << HEADER_SHIFT;
48
49pub const TAG_HEAP_REF: u8 = 1;
51pub const TAG_INT32: u8 = 2;
53pub const TAG_UNDEFINED: u8 = 3;
55pub const TAG_NULL: u8 = 4;
57pub const TAG_BOOLEAN: u8 = 5;
59pub const TAG_HOLE: u8 = 6;
61pub const TAG_UNINITIALIZED: u8 = 7;
63
64#[inline]
65const fn tag_of(bits: u64) -> u64 {
66 (bits >> TAG_SHIFT) & TAG_MASK
67}
68
69#[inline]
72const fn is_boxed(bits: u64) -> bool {
73 (bits & HEADER_MASK) == CANON_NAN && tag_of(bits) != 0
74}
75
76#[inline]
81const fn boxed(tag: u8, payload: u64) -> u64 {
82 CANON_NAN | ((tag as u64) << TAG_SHIFT) | payload
83}
84
85#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
90pub struct SlotId {
91 segment: NonZeroU16,
92 slot: NonZeroU32,
93}
94
95impl SlotId {
96 #[inline]
98 pub const fn new(segment: NonZeroU16, slot: NonZeroU32) -> Self {
99 Self { segment, slot }
100 }
101
102 #[inline]
104 pub const fn from_parts(segment: u16, slot: u32) -> Option<Self> {
105 match (NonZeroU16::new(segment), NonZeroU32::new(slot)) {
106 (Some(segment), Some(slot)) => Some(Self { segment, slot }),
107 _ => None,
108 }
109 }
110
111 #[inline]
113 pub const fn segment(self) -> u16 {
114 self.segment.get()
115 }
116
117 #[inline]
119 pub const fn slot(self) -> u32 {
120 self.slot.get()
121 }
122
123 #[inline]
125 const fn payload(self) -> u64 {
126 ((self.segment.get() as u64) << 32) | self.slot.get() as u64
127 }
128}
129
130#[repr(transparent)]
135#[derive(Clone, Copy, PartialEq, Eq, Hash)]
136pub struct Value(u64);
137
138#[derive(Clone, Copy, PartialEq, Debug)]
140pub enum Decoded {
141 Number(f64),
143 HeapRef(SlotId),
145 Int32(u32),
147 Undefined,
149 Null,
151 Boolean(bool),
153 Hole,
155 Uninitialized,
157}
158
159impl Value {
160 pub const CANON_NAN: u64 = CANON_NAN;
162
163 pub const UNDEFINED: Value = Value(boxed(TAG_UNDEFINED, 0));
165 pub const NULL: Value = Value(boxed(TAG_NULL, 0));
167 pub const HOLE: Value = Value(boxed(TAG_HOLE, 0));
169 pub const UNINITIALIZED: Value = Value(boxed(TAG_UNINITIALIZED, 0));
171 pub const FALSE: Value = Value(boxed(TAG_BOOLEAN, 0));
173 pub const TRUE: Value = Value(boxed(TAG_BOOLEAN, 1));
175
176 #[inline]
178 pub const fn boolean(value: bool) -> Value {
179 Value(boxed(TAG_BOOLEAN, value as u64))
180 }
181
182 #[inline]
184 pub const fn int32(value: u32) -> Value {
185 Value(boxed(TAG_INT32, value as u64))
186 }
187
188 #[inline]
190 pub const fn heap_ref(id: SlotId) -> Value {
191 Value(boxed(TAG_HEAP_REF, id.payload()))
192 }
193
194 #[inline]
197 pub fn number(value: f64) -> Value {
198 if value.is_nan() {
199 Value(CANON_NAN)
200 } else {
201 Value(value.to_bits())
202 }
203 }
204
205 #[inline]
208 pub const fn from_bits(bits: u64) -> Value {
209 Value(bits)
210 }
211
212 #[inline]
214 pub const fn to_bits(self) -> u64 {
215 self.0
216 }
217
218 #[inline]
220 pub const fn is_number(self) -> bool {
221 !is_boxed(self.0)
222 }
223
224 #[inline]
227 pub const fn is_uninitialized(self) -> bool {
228 self.0 == Value::UNINITIALIZED.0
229 }
230
231 pub const fn decode(self) -> Option<Decoded> {
234 let bits = self.0;
235 if !is_boxed(bits) {
236 return Some(Decoded::Number(f64::from_bits(bits)));
237 }
238 let payload = bits & PAYLOAD_MASK;
239 let upper = (payload >> 32) as u16;
240 let lower = payload as u32;
241 match tag_of(bits) as u8 {
242 TAG_HEAP_REF => match SlotId::from_parts(upper, lower) {
243 Some(id) => Some(Decoded::HeapRef(id)),
244 None => None,
245 },
246 TAG_INT32 => {
247 if upper == 0 {
248 Some(Decoded::Int32(lower))
249 } else {
250 None
251 }
252 }
253 TAG_UNDEFINED => {
254 if payload == 0 {
255 Some(Decoded::Undefined)
256 } else {
257 None
258 }
259 }
260 TAG_NULL => {
261 if payload == 0 {
262 Some(Decoded::Null)
263 } else {
264 None
265 }
266 }
267 TAG_BOOLEAN => match payload {
268 0 => Some(Decoded::Boolean(false)),
269 1 => Some(Decoded::Boolean(true)),
270 _ => None,
271 },
272 TAG_HOLE => {
273 if payload == 0 {
274 Some(Decoded::Hole)
275 } else {
276 None
277 }
278 }
279 TAG_UNINITIALIZED => {
280 if payload == 0 {
281 Some(Decoded::Uninitialized)
282 } else {
283 None
284 }
285 }
286 _ => None,
287 }
288 }
289
290 #[inline]
292 pub fn as_f64(self) -> Option<f64> {
293 match self.decode() {
294 Some(Decoded::Number(value)) => Some(value),
295 _ => None,
296 }
297 }
298
299 #[inline]
301 pub const fn as_int32(self) -> Option<u32> {
302 if is_boxed(self.0) && tag_of(self.0) as u8 == TAG_INT32 && (self.0 & UPPER_MASK) == 0 {
303 Some(self.0 as u32)
304 } else {
305 None
306 }
307 }
308
309 #[inline]
311 pub const fn as_bool(self) -> Option<bool> {
312 if is_boxed(self.0) && tag_of(self.0) as u8 == TAG_BOOLEAN {
313 match self.0 & PAYLOAD_MASK {
314 0 => Some(false),
315 1 => Some(true),
316 _ => None,
317 }
318 } else {
319 None
320 }
321 }
322
323 #[inline]
325 pub const fn as_heap_ref(self) -> Option<SlotId> {
326 if is_boxed(self.0) && tag_of(self.0) as u8 == TAG_HEAP_REF {
327 let payload = self.0 & PAYLOAD_MASK;
328 SlotId::from_parts((payload >> 32) as u16, payload as u32)
329 } else {
330 None
331 }
332 }
333}
334
335impl core::fmt::Debug for Value {
336 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
337 match self.decode() {
338 Some(decoded) => write!(f, "Value({decoded:?})"),
339 None => write!(f, "Value(malformed {:#018x})", self.0),
340 }
341 }
342}
343
344#[repr(u32)]
348#[derive(Clone, Copy, PartialEq, Eq, Debug)]
349pub enum CompletionTag {
350 Normal = 0,
352 Throw = 1,
354 Suspend = 2,
356 FatalTrap = 3,
358}
359
360impl CompletionTag {
361 #[inline]
363 pub const fn as_u32(self) -> u32 {
364 self as u32
365 }
366
367 #[inline]
369 pub const fn from_u32(code: u32) -> Option<CompletionTag> {
370 match code {
371 0 => Some(CompletionTag::Normal),
372 1 => Some(CompletionTag::Throw),
373 2 => Some(CompletionTag::Suspend),
374 3 => Some(CompletionTag::FatalTrap),
375 _ => None,
376 }
377 }
378}
379
380#[repr(C)]
382#[derive(Clone, Copy, PartialEq, Eq, Debug)]
383pub struct Completion {
384 pub value: Value,
386}
387
388impl Completion {
389 #[inline]
391 pub const fn new(value: Value) -> Completion {
392 Completion { value }
393 }
394}
395
396#[repr(C)]
405#[derive(Clone, Copy, Debug)]
406pub struct ShadowFrame {
407 pub previous: *mut ShadowFrame,
409 pub bytecode_pc: u32,
411 pub module_id: u32,
413 pub handles: *mut Value,
415 pub handle_len: u16,
417 _pad1: [u8; 6],
418}
419
420impl ShadowFrame {
421 #[inline]
423 pub fn new(
424 previous: *mut ShadowFrame,
425 bytecode_pc: u32,
426 module_id: u32,
427 handles: *mut Value,
428 handle_len: u16,
429 ) -> ShadowFrame {
430 ShadowFrame {
431 previous,
432 bytecode_pc,
433 module_id,
434 handles,
435 handle_len,
436 _pad1: [0; 6],
437 }
438 }
439}
440
441const _: () = {
444 use core::mem::{align_of, offset_of, size_of};
445
446 assert!(size_of::<Value>() == 8);
448 assert!(align_of::<Value>() == 8);
449
450 assert!(Value::CANON_NAN == 0x7ff8_0000_0000_0000);
452 assert!(Value::CANON_NAN == 4095u64 << 51);
453
454 assert!(size_of::<Completion>() == 8);
456 assert!(align_of::<Completion>() == 8);
457 assert!(offset_of!(Completion, value) == 0);
458
459 assert!(size_of::<CompletionTag>() == 4);
461 assert!(align_of::<CompletionTag>() == 4);
462
463 assert!(size_of::<ShadowFrame>() == 32);
465 assert!(align_of::<ShadowFrame>() == 8);
466 assert!(offset_of!(ShadowFrame, previous) == 0);
467 assert!(offset_of!(ShadowFrame, bytecode_pc) == 8);
468 assert!(offset_of!(ShadowFrame, module_id) == 12);
469 assert!(offset_of!(ShadowFrame, handles) == 16);
470 assert!(offset_of!(ShadowFrame, handle_len) == 24);
471};
472
473pub mod native_bridge;
476pub use native_bridge::*;
477
478#[cfg(test)]
479mod tests {
480 use super::*;
481
482 fn slot(segment: u16, slot: u32) -> SlotId {
483 SlotId::from_parts(segment, slot).expect("nonzero parts")
484 }
485
486 #[test]
487 fn canonical_singleton_bits_match_lean_layout() {
488 assert_eq!(Value::UNDEFINED.to_bits(), 0x7ffb_0000_0000_0000);
490 assert_eq!(Value::NULL.to_bits(), 0x7ffc_0000_0000_0000);
491 assert_eq!(Value::FALSE.to_bits(), 0x7ffd_0000_0000_0000);
492 assert_eq!(Value::TRUE.to_bits(), 0x7ffd_0000_0000_0001);
493 assert_eq!(Value::HOLE.to_bits(), 0x7ffe_0000_0000_0000);
494 assert_eq!(Value::UNINITIALIZED.to_bits(), 0x7fff_0000_0000_0000);
495 assert_eq!(Value::int32(0).to_bits(), 0x7ffa_0000_0000_0000);
496 assert_eq!(Value::heap_ref(slot(1, 1)).to_bits(), 0x7ff9_0001_0000_0001);
497 }
498
499 #[test]
500 fn decode_is_left_inverse_of_encode() {
501 let cases = [
502 (Value::UNDEFINED, Decoded::Undefined),
503 (Value::NULL, Decoded::Null),
504 (Value::HOLE, Decoded::Hole),
505 (Value::UNINITIALIZED, Decoded::Uninitialized),
506 (Value::boolean(false), Decoded::Boolean(false)),
507 (Value::boolean(true), Decoded::Boolean(true)),
508 (Value::int32(0), Decoded::Int32(0)),
509 (Value::int32(u32::MAX), Decoded::Int32(u32::MAX)),
510 (Value::int32(0x1234_5678), Decoded::Int32(0x1234_5678)),
511 (Value::heap_ref(slot(1, 1)), Decoded::HeapRef(slot(1, 1))),
512 (
513 Value::heap_ref(slot(u16::MAX, u32::MAX)),
514 Decoded::HeapRef(slot(u16::MAX, u32::MAX)),
515 ),
516 ];
517 for (value, expected) in cases {
518 assert_eq!(value.decode(), Some(expected), "{value:?}");
519 let reencoded = match expected {
521 Decoded::Undefined => Value::UNDEFINED,
522 Decoded::Null => Value::NULL,
523 Decoded::Hole => Value::HOLE,
524 Decoded::Uninitialized => Value::UNINITIALIZED,
525 Decoded::Boolean(b) => Value::boolean(b),
526 Decoded::Int32(v) => Value::int32(v),
527 Decoded::HeapRef(id) => Value::heap_ref(id),
528 Decoded::Number(x) => Value::number(x),
529 };
530 assert_eq!(reencoded.to_bits(), value.to_bits());
531 }
532 }
533
534 #[test]
535 fn numbers_are_not_boxed_and_roundtrip() {
536 for x in [
537 0.0f64,
538 -0.0,
539 1.5,
540 -2.25,
541 f64::MAX,
542 f64::MIN,
543 f64::INFINITY,
544 f64::NEG_INFINITY,
545 ] {
546 let value = Value::number(x);
547 assert!(value.is_number(), "{x} should be an unboxed number");
548 assert_eq!(value.decode(), Some(Decoded::Number(x)));
549 assert_eq!(value.as_f64(), Some(x));
550 assert_eq!(value.to_bits(), x.to_bits());
551 }
552 }
553
554 #[test]
555 fn every_nan_canonicalizes_and_stays_a_number() {
556 for raw in [
557 f64::NAN.to_bits(),
558 0x7ff8_0000_0000_0001,
559 0xffff_ffff_ffff_ffff,
560 0x7ff0_0000_0000_0001, ] {
562 let value = Value::number(f64::from_bits(raw));
563 assert_eq!(value.to_bits(), Value::CANON_NAN);
564 assert!(value.is_number());
565 match value.decode() {
566 Some(Decoded::Number(x)) => assert!(x.is_nan()),
567 other => panic!("expected NaN number, got {other:?}"),
568 }
569 }
570 }
571
572 #[test]
573 fn canonical_nan_word_decodes_as_number_not_boxed() {
574 let value = Value::from_bits(Value::CANON_NAN);
576 assert!(value.is_number());
577 assert!(matches!(value.decode(), Some(Decoded::Number(_))));
578 }
579
580 #[test]
581 fn malformed_boxed_payloads_are_rejected() {
582 assert_eq!(
584 Value::from_bits(boxed(TAG_INT32, 0x0001_0000_0000)).decode(),
585 None
586 );
587 assert_eq!(Value::from_bits(boxed(TAG_UNDEFINED, 1)).decode(), None);
589 assert_eq!(Value::from_bits(boxed(TAG_NULL, 1)).decode(), None);
590 assert_eq!(Value::from_bits(boxed(TAG_HOLE, 1)).decode(), None);
591 assert_eq!(Value::from_bits(boxed(TAG_UNINITIALIZED, 1)).decode(), None);
592 assert_eq!(Value::from_bits(boxed(TAG_BOOLEAN, 2)).decode(), None);
594 assert_eq!(
596 Value::from_bits(boxed(TAG_HEAP_REF, 0x0000_0000_0001)).decode(),
597 None
598 );
599 assert_eq!(
600 Value::from_bits(boxed(TAG_HEAP_REF, 0x0001_0000_0000)).decode(),
601 None
602 );
603 }
604
605 #[test]
606 fn distinct_tags_never_share_an_encoding() {
607 let payload = 0u64;
609 let tags = [
610 TAG_HEAP_REF,
611 TAG_INT32,
612 TAG_UNDEFINED,
613 TAG_NULL,
614 TAG_BOOLEAN,
615 TAG_HOLE,
616 TAG_UNINITIALIZED,
617 ];
618 for (i, &left) in tags.iter().enumerate() {
619 for &right in &tags[i + 1..] {
620 assert_ne!(boxed(left, payload), boxed(right, payload));
621 }
622 }
623 }
624
625 #[test]
626 fn slot_id_rejects_zero_parts() {
627 assert!(SlotId::from_parts(0, 1).is_none());
628 assert!(SlotId::from_parts(1, 0).is_none());
629 assert!(SlotId::from_parts(0, 0).is_none());
630 let id = slot(7, 9);
631 assert_eq!(id.segment(), 7);
632 assert_eq!(id.slot(), 9);
633 }
634
635 #[test]
636 fn typed_accessors_agree_with_decode() {
637 assert_eq!(Value::int32(42).as_int32(), Some(42));
638 assert_eq!(Value::UNDEFINED.as_int32(), None);
639 assert_eq!(Value::boolean(true).as_bool(), Some(true));
640 assert_eq!(Value::boolean(false).as_bool(), Some(false));
641 assert_eq!(Value::int32(1).as_bool(), None);
642 let id = slot(3, 4);
643 assert_eq!(Value::heap_ref(id).as_heap_ref(), Some(id));
644 assert_eq!(Value::NULL.as_heap_ref(), None);
645 assert!(Value::UNINITIALIZED.is_uninitialized());
646 assert!(!Value::HOLE.is_uninitialized());
647 }
648
649 #[test]
650 fn completion_tag_roundtrips_and_rejects_out_of_range() {
651 for tag in [
652 CompletionTag::Normal,
653 CompletionTag::Throw,
654 CompletionTag::Suspend,
655 CompletionTag::FatalTrap,
656 ] {
657 assert_eq!(CompletionTag::from_u32(tag.as_u32()), Some(tag));
658 }
659 assert_eq!(CompletionTag::Normal.as_u32(), 0);
660 assert_eq!(CompletionTag::FatalTrap.as_u32(), 3);
661 assert_eq!(CompletionTag::from_u32(4), None);
662 assert_eq!(CompletionTag::from_u32(u32::MAX), None);
663 }
664
665 #[test]
666 fn shadow_frame_new_zeroes_padding_and_keeps_fields() {
667 let mut register = Value::UNINITIALIZED;
668 let frame = ShadowFrame::new(core::ptr::null_mut(), 12, 7, &mut register, 1);
669 assert!(frame.previous.is_null());
670 assert_eq!(frame.bytecode_pc, 12);
671 assert_eq!(frame.module_id, 7);
672 assert_eq!(frame.handle_len, 1);
673 assert_eq!(frame._pad1, [0; 6]);
674 assert!(core::ptr::eq(frame.handles, &raw mut register));
675 }
676
677 #[test]
678 fn completion_wraps_value() {
679 let completion = Completion::new(Value::int32(5));
680 assert_eq!(completion.value.as_int32(), Some(5));
681 }
682}