1use std::cmp::Ordering as CmpOrdering;
9use std::fmt::Write as _;
10use std::hash::Hasher;
11
12use num_bigint::BigInt;
13use num_integer::Integer;
14use num_traits::{Signed, ToPrimitive, Zero};
15use rustc_hash::FxHasher;
16
17use crate::class::{heap_class, ids, Class, ObjKind};
18use crate::dict::Probe;
19use crate::mutator::{panic, Mutator, RtResult, Trap};
20use crate::object::*;
21use crate::ownership::{read_dict_unchecked, read_list, with_read};
22use crate::symbol::{intern, Sym};
23use crate::value::{fits_small, Value};
24
25pub trait Interop {
27 fn mutator(&mut self) -> &mut Mutator;
28 fn call_method(&mut self, recv: Value, name: Sym, args: &[Value]) -> RtResult<Option<Value>>;
31}
32
33pub struct Syms {
34 pub eq: Sym,
35 pub hash: Sym,
36 pub cmp: Sym,
37 pub to_s: Sym,
38 pub inspect: Sym,
39}
40
41pub static SYMS: std::sync::LazyLock<Syms> = std::sync::LazyLock::new(|| Syms {
42 eq: intern("=="),
43 hash: intern("hash"),
44 cmp: intern("<=>"),
45 to_s: intern("to_s"),
46 inspect: intern("inspect"),
47});
48
49pub fn hash_bytes(b: &[u8]) -> u64 {
50 let mut h = FxHasher::default();
51 h.write(b);
52 h.write_usize(b.len());
53 mix(h.finish())
54}
55
56#[inline]
57fn mix(mut x: u64) -> u64 {
58 x ^= x >> 30;
60 x = x.wrapping_mul(0xbf58_476d_1ce4_e5b9);
61 x ^= x >> 27;
62 x = x.wrapping_mul(0x94d0_49bb_1331_11eb);
63 x ^ (x >> 31)
64}
65
66#[inline]
67pub fn hash_int(i: i64) -> u64 {
68 mix(i as u64 ^ 0x5151_5151)
69}
70
71fn user_class(h: &catallaxy_gc::Header) -> &'static Class {
72 unsafe { heap_class(h) }
73}
74
75fn is_user_instance(cls: &Class) -> bool {
76 cls.kind == ObjKind::Instance && cls.id >= ids::FIRST_USER
77}
78
79pub fn is_int(v: Value) -> bool {
83 v.is_small_int() || (v.is_ptr() && kind_of(v) == ObjKind::BigInt)
84}
85
86fn bigint_ref(v: Value) -> Option<&'static BigInt> {
88 if v.is_ptr() && kind_of(v) == ObjKind::BigInt {
89 Some(&unsafe { as_bigint(v) }.value)
90 } else {
91 None
92 }
93}
94
95pub fn to_bigint(v: Value) -> BigInt {
96 if v.is_small_int() {
97 BigInt::from(v.as_small_int())
98 } else {
99 bigint_ref(v).cloned().unwrap_or_default()
100 }
101}
102
103pub fn int_to_f64(v: Value) -> f64 {
104 if v.is_small_int() {
105 v.as_small_int() as f64
106 } else {
107 bigint_ref(v).and_then(|b| b.to_f64()).unwrap_or(f64::NAN)
108 }
109}
110
111#[inline]
113pub fn int_as_i64(v: Value) -> Option<i64> {
114 if v.is_small_int() {
115 Some(v.as_small_int())
116 } else {
117 bigint_ref(v).and_then(|b| b.to_i64())
118 }
119}
120
121#[inline]
122pub fn int_add(m: &mut Mutator, a: Value, b: Value) -> Value {
123 if Value::both_small_ints(a, b) {
124 return m.int(a.as_small_int() + b.as_small_int());
125 }
126 m.int_from_big(to_bigint(a) + to_bigint(b))
127}
128
129#[inline]
130pub fn int_sub(m: &mut Mutator, a: Value, b: Value) -> Value {
131 if Value::both_small_ints(a, b) {
132 return m.int(a.as_small_int() - b.as_small_int());
133 }
134 m.int_from_big(to_bigint(a) - to_bigint(b))
135}
136
137#[inline]
138pub fn int_mul(m: &mut Mutator, a: Value, b: Value) -> Value {
139 if Value::both_small_ints(a, b) {
140 if let Some(p) = a.as_small_int().checked_mul(b.as_small_int()) {
141 return m.int(p);
142 }
143 }
144 m.int_from_big(to_bigint(a) * to_bigint(b))
145}
146
147fn zero_div<T>() -> RtResult<T> {
148 panic("ZeroDivisionError: divided by 0")
149}
150
151pub fn int_div(m: &mut Mutator, a: Value, b: Value) -> RtResult<Value> {
153 if Value::both_small_ints(a, b) {
154 let (x, y) = (a.as_small_int(), b.as_small_int());
155 if y == 0 {
156 return zero_div();
157 }
158 return Ok(m.int(Integer::div_floor(&x, &y)));
159 }
160 let y = to_bigint(b);
161 if y.is_zero() {
162 return zero_div();
163 }
164 Ok(m.int_from_big(to_bigint(a).div_floor(&y)))
165}
166
167pub fn int_mod(m: &mut Mutator, a: Value, b: Value) -> RtResult<Value> {
169 if Value::both_small_ints(a, b) {
170 let (x, y) = (a.as_small_int(), b.as_small_int());
171 if y == 0 {
172 return zero_div();
173 }
174 return Ok(m.int(Integer::mod_floor(&x, &y)));
175 }
176 let y = to_bigint(b);
177 if y.is_zero() {
178 return zero_div();
179 }
180 Ok(m.int_from_big(to_bigint(a).mod_floor(&y)))
181}
182
183pub fn int_pow(m: &mut Mutator, a: Value, b: Value) -> RtResult<Value> {
184 let Some(e) = int_as_i64(b) else { return panic("exponent too large") };
185 if e < 0 {
186 return panic("negative exponent for Int ** Int; use Float");
187 }
188 if e > u32::MAX as i64 {
189 return panic("exponent too large");
190 }
191 if let Some(x) = a.is_small_int().then(|| a.as_small_int()) {
192 if let Some(p) = x.checked_pow(e as u32) {
193 return Ok(m.int(p));
194 }
195 }
196 Ok(m.int_from_big(num_traits::pow(to_bigint(a), e as usize)))
197}
198
199pub fn int_neg(m: &mut Mutator, a: Value) -> Value {
200 if a.is_small_int() {
201 return m.int(-a.as_small_int());
202 }
203 m.int_from_big(-to_bigint(a))
204}
205
206pub fn int_bitop(m: &mut Mutator, op: u8, a: Value, b: Value) -> RtResult<Value> {
207 if Value::both_small_ints(a, b) {
208 let (x, y) = (a.as_small_int(), b.as_small_int());
209 let r = match op {
210 b'&' => x & y,
211 b'|' => x | y,
212 b'^' => x ^ y,
213 b'<' => {
214 if y < 0 {
215 return panic("negative shift count");
216 }
217 if y < 16 && fits_small(x << y) && (x << y) >> y == x {
218 x << y
219 } else {
220 return Ok(m.int_from_big(BigInt::from(x) << (y as usize)));
221 }
222 }
223 b'>' => {
224 if y < 0 {
225 return panic("negative shift count");
226 }
227 x >> y.min(63)
228 }
229 _ => unreachable!(),
230 };
231 return Ok(m.int(r));
232 }
233 let (x, y) = (to_bigint(a), to_bigint(b));
234 let r = match op {
235 b'&' => x & y,
236 b'|' => x | y,
237 b'^' => x ^ y,
238 b'<' | b'>' => {
239 let Some(n) = y.to_usize() else { return panic("invalid shift count") };
240 if op == b'<' {
241 x << n
242 } else {
243 x >> n
244 }
245 }
246 _ => unreachable!(),
247 };
248 Ok(m.int_from_big(r))
249}
250
251pub fn int_cmp(a: Value, b: Value) -> CmpOrdering {
252 if Value::both_small_ints(a, b) {
253 return a.as_small_int().cmp(&b.as_small_int());
254 }
255 to_bigint(a).cmp(&to_bigint(b))
256}
257
258pub fn float_to_int(m: &mut Mutator, f: f64) -> RtResult<Value> {
260 if !f.is_finite() {
261 return panic(format!("FloatDomainError: {}", format_float(f)));
262 }
263 let t = f.trunc();
264 if t.abs() < 1e15 {
265 return Ok(m.int(t as i64));
266 }
267 use num_traits::FromPrimitive;
268 Ok(m.int_from_big(BigInt::from_f64(t).unwrap()))
269}
270
271fn num_f64(v: Value) -> Option<f64> {
272 if v.is_float() {
273 Some(v.as_f64())
274 } else if is_int(v) {
275 Some(int_to_f64(v))
276 } else {
277 None
278 }
279}
280
281pub fn format_float(f: f64) -> String {
286 if f.is_nan() {
287 return "NaN".into();
288 }
289 if f.is_infinite() {
290 return if f > 0.0 { "Infinity".into() } else { "-Infinity".into() };
291 }
292 let a = f.abs();
293 if a != 0.0 && !(1e-4..1e16).contains(&a) {
294 let s = format!("{:e}", f);
296 let (mant, exp) = s.split_once('e').unwrap();
297 let mant = if mant.contains('.') { mant.to_string() } else { format!("{mant}.0") };
298 let e: i32 = exp.parse().unwrap();
299 return format!("{mant}e{}{:02}", if e < 0 { '-' } else { '+' }, e.abs());
300 }
301 let s = format!("{}", f);
302 if s.contains('.') {
303 s
304 } else {
305 format!("{s}.0")
306 }
307}
308
309pub fn format_int(v: Value) -> String {
310 if v.is_small_int() {
311 v.as_small_int().to_string()
312 } else {
313 to_bigint(v).to_string()
314 }
315}
316
317pub fn quote_str(out: &mut String, s: &str) {
319 out.push('"');
320 for c in s.chars() {
321 match c {
322 '"' => out.push_str("\\\""),
323 '\\' => out.push_str("\\\\"),
324 '\n' => out.push_str("\\n"),
325 '\t' => out.push_str("\\t"),
326 '\r' => out.push_str("\\r"),
327 '\0' => out.push_str("\\0"),
328 '#' => out.push('#'),
329 c if (c as u32) < 0x20 => {
330 let _ = write!(out, "\\u{{{:x}}}", c as u32);
331 }
332 c => out.push(c),
333 }
334 }
335 out.push('"');
336}
337
338pub fn to_s(ix: &mut dyn Interop, v: Value) -> RtResult<String> {
340 if let Some(s) = str_of(v) {
341 return Ok(s.to_string());
342 }
343 let mut out = String::new();
344 write_value(ix, v, &mut out, false, 0)?;
345 Ok(out)
346}
347
348pub fn inspect(ix: &mut dyn Interop, v: Value) -> RtResult<String> {
350 let mut out = String::new();
351 write_value(ix, v, &mut out, true, 0)?;
352 Ok(out)
353}
354
355fn write_value(ix: &mut dyn Interop, v: Value, out: &mut String, quoted: bool, depth: usize) -> RtResult<()> {
356 if depth > 64 {
357 out.push_str("...");
358 return Ok(());
359 }
360 if v.is_float() {
361 out.push_str(&format_float(v.as_f64()));
362 return Ok(());
363 }
364 if v.is_small_int() {
365 let _ = write!(out, "{}", v.as_small_int());
366 return Ok(());
367 }
368 if v.is_nil() {
369 out.push_str("nil");
370 return Ok(());
371 }
372 if v.is_bool() {
373 out.push_str(if v.is_true() { "true" } else { "false" });
374 return Ok(());
375 }
376 if !v.is_ptr() {
377 out.push_str("<?>");
378 return Ok(());
379 }
380 let h = unsafe { &*v.as_ptr() };
381 let cls = user_class(h);
382 if cls.id >= ids::FIRST_USER || cls.kind == ObjKind::Instance && cls.id != ids::RANGE {
384 let name = if quoted && cls.lookup(SYMS.inspect).is_some() { SYMS.inspect } else { SYMS.to_s };
385 if cls.lookup(name).is_some() {
386 if let Some(r) = ix.call_method(v, name, &[])? {
387 match str_of(r) {
388 Some(s) => out.push_str(s),
389 None => return panic(format!("{}#{} must return a String", cls.name, name)),
390 }
391 return Ok(());
392 }
393 }
394 }
395 crate::stack::check()?;
396 let m = ix.mutator() as *mut Mutator;
397 let m = unsafe { &*m };
398 match cls.kind {
399 ObjKind::Str => {
400 let s = unsafe { as_str_obj(v) }.as_str();
401 if quoted {
402 quote_str(out, s)
403 } else {
404 out.push_str(s)
405 }
406 }
407 ObjKind::BigInt => out.push_str(&format_int(v)),
408 ObjKind::List => {
409 out.push('[');
410 let mut i = 0;
411 loop {
412 let item = read_list(m, v)?.get(i);
413 let Some(item) = item else { break };
414 if i > 0 {
415 out.push_str(", ");
416 }
417 write_value(ix, item, out, true, depth + 1)?;
418 i += 1;
419 }
420 out.push(']');
421 }
422 ObjKind::Tuple => {
423 out.push('(');
424 let elems = unsafe { as_tuple(v) }.elems();
425 for (i, &e) in elems.iter().enumerate() {
426 if i > 0 {
427 out.push_str(", ");
428 }
429 write_value(ix, e, out, true, depth + 1)?;
430 }
431 if elems.len() == 1 {
432 out.push(',');
433 }
434 out.push(')');
435 }
436 ObjKind::Dict | ObjKind::Set => {
438 let is_set = cls.kind == ObjKind::Set;
439 out.push_str(if is_set { "Set[" } else { "[" });
440 let n = unsafe { read_dict_unchecked(m, v, |t| t.raw_len()) }?;
441 let mut first = true;
442 for i in 0..n {
443 let e = unsafe { read_dict_unchecked(m, v, |t| t.raw_entry(i)) }?;
444 let Some(e) = e else { continue };
445 if !first {
446 out.push_str(", ");
447 }
448 first = false;
449 write_value(ix, e.key, out, true, depth + 1)?;
450 if !is_set {
451 out.push_str(": ");
452 write_value(ix, e.value, out, true, depth + 1)?;
453 }
454 }
455 if first && !is_set {
456 out.push(':');
457 }
458 out.push(']');
459 }
460 ObjKind::Instance if cls.id == ids::RANGE => {
461 let r = unsafe { as_instance(v) };
462 write_value(ix, r.get(0), out, true, depth + 1)?;
463 out.push_str(if r.get(2).is_true() { "..." } else { ".." });
464 write_value(ix, r.get(1), out, true, depth + 1)?;
465 }
466 ObjKind::Instance => {
467 let o = unsafe { as_instance(v) };
468 out.push_str(&cls.name);
469 let n = cls.nfields();
470 if cls.variant_tag.is_some() {
471 if n > 0 {
472 out.push('(');
473 for i in 0..n {
474 if i > 0 {
475 out.push_str(", ");
476 }
477 write_value(ix, o.get(i), out, true, depth + 1)?;
478 }
479 out.push(')');
480 }
481 } else {
482 out.push('(');
483 for i in 0..n {
484 if i > 0 {
485 out.push_str(", ");
486 }
487 let _ = write!(out, "{}: ", cls.field_names[i]);
488 let fv = if h.state() == catallaxy_gc::ObjState::Shared { o.get_acquire(i) } else { o.get(i) };
489 write_value(ix, fv, out, true, depth + 1)?;
490 }
491 out.push(')');
492 }
493 }
494 ObjKind::StringBuilder => {
495 let s = with_read(m, v, || unsafe { (*as_sb(v).buf.get()).clone() })?;
496 if quoted {
497 quote_str(out, &s)
498 } else {
499 out.push_str(&s)
500 }
501 }
502 ObjKind::Closure => out.push_str("#<fn>"),
503 _ => {
504 let _ = write!(out, "#<{}>", cls.name);
505 }
506 }
507 Ok(())
508}
509
510pub fn values_eq(ix: &mut dyn Interop, a: Value, b: Value) -> RtResult<bool> {
516 if a.0 == b.0 {
517 return Ok(!(a.is_float() && a.as_f64().is_nan()));
518 }
519 if a.is_float() || b.is_float() || a.is_small_int() || b.is_small_int() {
520 return Ok(match (num_f64(a), num_f64(b)) {
521 (Some(x), Some(y)) if a.is_float() || b.is_float() => x == y,
522 _ => is_int(a) && is_int(b) && int_cmp(a, b) == CmpOrdering::Equal,
523 });
524 }
525 if !a.is_ptr() || !b.is_ptr() {
526 return Ok(false);
527 }
528 let (ha, hb) = unsafe { (&*a.as_ptr(), &*b.as_ptr()) };
529 let (ca, cb) = (user_class(ha), user_class(hb));
530 if is_user_instance(ca) && ca.lookup(SYMS.eq).is_some() {
531 if let Some(r) = ix.call_method(a, SYMS.eq, &[b])? {
532 return Ok(r.is_true());
533 }
534 }
535 if ca.kind != cb.kind {
536 return Ok(false);
537 }
538 let m = unsafe { &*(ix.mutator() as *mut Mutator) };
539 match ca.kind {
540 ObjKind::Str => Ok(unsafe { as_str_obj(a).as_str() == as_str_obj(b).as_str() }),
541 ObjKind::BigInt => Ok(unsafe { as_bigint(a).value == as_bigint(b).value }),
542 ObjKind::Tuple => {
543 crate::stack::check()?;
544 let (ta, tb) = unsafe { (as_tuple(a), as_tuple(b)) };
545 if ta.len != tb.len {
546 return Ok(false);
547 }
548 for i in 0..ta.len {
549 if !values_eq(ix, ta.elems()[i], tb.elems()[i])? {
550 return Ok(false);
551 }
552 }
553 Ok(true)
554 }
555 ObjKind::List => {
556 crate::stack::check()?;
557 let len = |v: Value| read_list(m, v).map(|l| l.len());
558 if len(a)? != len(b)? {
559 return Ok(false);
560 }
561 let mut i = 0;
562 loop {
563 let x = read_list(m, a)?.get(i);
564 let y = read_list(m, b)?.get(i);
565 match (x, y) {
566 (Some(x), Some(y)) => {
567 if !values_eq(ix, x, y)? {
568 return Ok(false);
569 }
570 }
571 (None, None) => return Ok(true),
572 _ => return Ok(false),
573 }
574 i += 1;
575 }
576 }
577 ObjKind::Dict | ObjKind::Set => {
579 crate::stack::check()?;
580 let len = |v: Value| unsafe { read_dict_unchecked(m, v, |t| t.len()) };
581 if len(a)? != len(b)? {
582 return Ok(false);
583 }
584 let n = unsafe { read_dict_unchecked(m, a, |t| t.raw_len()) }?;
585 for i in 0..n {
586 let e = unsafe { read_dict_unchecked(m, a, |t| t.raw_entry(i)) }?;
587 let Some(e) = e else { continue };
588 match dict_lookup(ix, b, e.key)? {
589 None => return Ok(false),
590 Some(idx) => {
591 if ca.kind == ObjKind::Dict {
592 let bv = unsafe { read_dict_unchecked(m, b, |t| t.entry(idx).value) }?;
593 if !values_eq(ix, e.value, bv)? {
594 return Ok(false);
595 }
596 }
597 }
598 }
599 }
600 Ok(true)
601 }
602 ObjKind::Instance => {
603 if ca.id != cb.id || !(ca.variant_tag.is_some() || ca.id == ids::RANGE) {
605 return Ok(false);
606 }
607 crate::stack::check()?;
608 let (oa, ob) = unsafe { (as_instance(a), as_instance(b)) };
609 for i in 0..ca.nfields() {
610 if !values_eq(ix, oa.get(i), ob.get(i))? {
611 return Ok(false);
612 }
613 }
614 Ok(true)
615 }
616 _ => Ok(false),
617 }
618}
619
620pub fn hash_value(ix: &mut dyn Interop, v: Value) -> RtResult<u64> {
622 if v.is_small_int() {
623 return Ok(hash_int(v.as_small_int()));
624 }
625 if v.is_float() {
626 let f = v.as_f64();
627 if f.fract() == 0.0 && f.abs() < 1e15 {
628 return Ok(hash_int(f as i64));
629 }
630 return Ok(mix(f.to_bits()));
631 }
632 if !v.is_ptr() {
633 return Ok(mix(v.0));
634 }
635 let h = unsafe { &*v.as_ptr() };
636 let cls = user_class(h);
637 let m = unsafe { &*(ix.mutator() as *mut Mutator) };
638 match cls.kind {
639 ObjKind::Str => Ok(unsafe { as_str_obj(v) }.hash_code()),
640 ObjKind::BigInt => {
641 let b = &unsafe { as_bigint(v) }.value;
642 Ok(hash_bytes(&b.to_signed_bytes_le()))
643 }
644 ObjKind::Tuple => {
645 crate::stack::check()?;
646 let mut acc = 0x7475_706c_u64;
647 for &e in unsafe { as_tuple(v) }.elems() {
648 acc = mix(acc ^ hash_value(ix, e)?);
649 }
650 Ok(acc)
651 }
652 ObjKind::List => {
653 crate::stack::check()?;
654 let mut acc = 0x6c69_7374_u64;
655 let mut i = 0;
656 loop {
657 let x = read_list(m, v)?.get(i);
658 let Some(x) = x else { break };
659 acc = mix(acc ^ hash_value(ix, x)?);
660 i += 1;
661 }
662 Ok(acc)
663 }
664 ObjKind::Dict | ObjKind::Set => {
666 crate::stack::check()?;
667 let mut acc = 0x6469_6374_u64;
668 let n = unsafe { read_dict_unchecked(m, v, |t| t.raw_len()) }?;
669 for i in 0..n {
670 let e = unsafe { read_dict_unchecked(m, v, |t| t.raw_entry(i)) }?;
671 if let Some(e) = e {
672 let hv = if cls.kind == ObjKind::Dict { hash_value(ix, e.value)? } else { 0 };
673 acc ^= mix(e.hash ^ hv.rotate_left(17));
674 }
675 }
676 Ok(acc)
677 }
678 ObjKind::Instance => {
679 if is_user_instance(cls) && cls.lookup(SYMS.hash).is_some() {
680 if let Some(r) = ix.call_method(v, SYMS.hash, &[])? {
681 return Ok(if r.is_small_int() { hash_int(r.as_small_int()) } else { mix(r.0) });
682 }
683 }
684 if cls.variant_tag.is_some() || cls.id == ids::RANGE {
685 crate::stack::check()?;
686 let o = unsafe { as_instance(v) };
687 let mut acc = mix(cls.id as u64);
688 for i in 0..cls.nfields() {
689 acc = mix(acc ^ hash_value(ix, o.get(i))?);
690 }
691 return Ok(acc);
692 }
693 Ok(mix(h.identity_hash() as u64))
694 }
695 _ => Ok(mix(h.identity_hash() as u64)),
696 }
697}
698
699pub fn dict_lookup(ix: &mut dyn Interop, dict: Value, key: Value) -> RtResult<Option<usize>> {
701 let hash = hash_value(ix, key)?;
702 dict_lookup_hashed(ix, dict, key, hash)
703}
704
705pub fn dict_lookup_hashed(ix: &mut dyn Interop, dict: Value, key: Value, hash: u64) -> RtResult<Option<usize>> {
707 dict_of(dict)?;
708 unsafe { dict_lookup_hashed_unchecked(ix, dict, key, hash) }
710}
711
712pub unsafe fn dict_lookup_hashed_unchecked(
717 ix: &mut dyn Interop,
718 dict: Value,
719 key: Value,
720 hash: u64,
721) -> RtResult<Option<usize>> {
722 let m = unsafe { &*(ix.mutator() as *mut Mutator) };
723 loop {
725 let (probe, version) = unsafe { read_dict_unchecked(m, dict, |t| (t.probe(hash, key), t.version())) }?;
726 match probe {
727 Probe::Found(i) => return Ok(Some(i)),
728 Probe::Absent => return Ok(None),
729 Probe::Check(cands) => {
730 let mut found = None;
731 for idx in cands {
732 let k = unsafe { read_dict_unchecked(m, dict, |t| t.entry(idx).key) }?;
733 if values_eq(ix, k, key)? {
734 found = Some(idx);
735 break;
736 }
737 }
738 let still = unsafe { read_dict_unchecked(m, dict, |t| t.version()) }?;
739 if still == version {
740 return Ok(found);
741 }
742 }
744 }
745 }
746}
747
748pub fn compare(ix: &mut dyn Interop, a: Value, b: Value) -> RtResult<Option<CmpOrdering>> {
750 if is_int(a) && is_int(b) {
751 return Ok(Some(int_cmp(a, b)));
752 }
753 if let (Some(x), Some(y)) = (num_f64(a), num_f64(b)) {
754 return Ok(x.partial_cmp(&y));
755 }
756 if !a.is_ptr() || !b.is_ptr() {
757 if a.is_bool() && b.is_bool() {
758 return Ok(Some(a.is_true().cmp(&b.is_true())));
759 }
760 return Ok(None);
761 }
762 let (ca, cb) = unsafe { (heap_class(a.as_ptr()), heap_class(b.as_ptr())) };
763 if is_user_instance(ca) && ca.lookup(SYMS.cmp).is_some() {
764 if let Some(r) = ix.call_method(a, SYMS.cmp, &[b])? {
765 return Ok(int_as_i64(r).map(|i| i.cmp(&0)));
766 }
767 }
768 if ca.kind != cb.kind {
769 return Ok(None);
770 }
771 let m = unsafe { &*(ix.mutator() as *mut Mutator) };
772 match ca.kind {
773 ObjKind::Str => Ok(Some(unsafe { as_str_obj(a).as_str().cmp(as_str_obj(b).as_str()) })),
774 ObjKind::Tuple | ObjKind::List => {
775 crate::stack::check()?;
776 let get = |v: Value, i: usize| -> RtResult<Option<Value>> {
777 if ca.kind == ObjKind::Tuple {
778 Ok(unsafe { as_tuple(v) }.elems().get(i).copied())
779 } else {
780 read_list(m, v).map(|l| l.get(i))
781 }
782 };
783 let mut i = 0;
784 loop {
785 match (get(a, i)?, get(b, i)?) {
786 (None, None) => return Ok(Some(CmpOrdering::Equal)),
787 (None, Some(_)) => return Ok(Some(CmpOrdering::Less)),
788 (Some(_), None) => return Ok(Some(CmpOrdering::Greater)),
789 (Some(x), Some(y)) => match compare(ix, x, y)? {
790 Some(CmpOrdering::Equal) => {}
791 other => return Ok(other),
792 },
793 }
794 i += 1;
795 }
796 }
797 ObjKind::Instance if ca.variant_tag.is_some() && ca.enum_id == cb.enum_id => {
798 let (ta, tb) = (ca.variant_tag.unwrap(), cb.variant_tag.unwrap());
799 if ta != tb {
800 return Ok(Some(ta.cmp(&tb)));
801 }
802 crate::stack::check()?;
803 let (oa, ob) = unsafe { (as_instance(a), as_instance(b)) };
804 for i in 0..ca.nfields() {
805 match compare(ix, oa.get(i), ob.get(i))? {
806 Some(CmpOrdering::Equal) => {}
807 other => return Ok(other),
808 }
809 }
810 Ok(Some(CmpOrdering::Equal))
811 }
812 _ => Ok(None),
813 }
814}
815
816pub fn compare_or_panic(ix: &mut dyn Interop, a: Value, b: Value) -> RtResult<CmpOrdering> {
818 match compare(ix, a, b)? {
819 Some(o) => Ok(o),
820 None => Err(Trap::Panic(format!(
821 "ArgumentError: comparison of {} with {} failed",
822 crate::class::class_of(a).name,
823 crate::class::class_of(b).name
824 ))),
825 }
826}
827
828pub fn sign_of(v: Value) -> i64 {
829 if v.is_small_int() {
830 v.as_small_int().signum()
831 } else {
832 let b = to_bigint(v);
833 if b.is_negative() {
834 -1
835 } else if b.is_zero() {
836 0
837 } else {
838 1
839 }
840 }
841}