use crate::panic::{GoPanic, go_panic};
use crate::place::Ptr;
use crate::trace::{Trace, Tracer};
use crate::value::GoValue;
use alloc::format;
use alloc::vec::Vec;
pub type MethodId = u32;
#[derive(Clone, Copy)]
pub struct ErasedFn(*const ());
unsafe impl Sync for ErasedFn {}
unsafe impl Send for ErasedFn {}
impl ErasedFn {
pub const fn new(code: *const ()) -> Self {
ErasedFn(code)
}
}
pub struct TypeDesc {
pub name: &'static str,
pub methods: &'static [(MethodId, ErasedFn)],
pub equal: Option<fn(Data, Data) -> bool>,
pub hash: Option<fn(Data) -> u64>,
pub print: fn(Data, &mut Vec<u8>),
}
impl TypeDesc {
fn method(&self, id: MethodId) -> Option<ErasedFn> {
self.methods
.binary_search_by_key(&id, |(i, _)| *i)
.ok()
.map(|i| self.methods[i].1)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Data(usize);
impl Data {
pub const NONE: Data = Data(0);
#[inline]
pub fn of<P>(p: Ptr<P>) -> Data {
Data(p.addr() as usize)
}
#[inline]
pub fn cast<P>(self) -> Ptr<P> {
unsafe { Ptr::from_addr(self.0) }
}
pub fn addr(self) -> u64 {
self.0 as u64
}
}
impl Trace for Data {
#[inline]
fn trace(&self, t: &mut Tracer<'_>) {
t.edge(self.0);
}
}
#[derive(Clone, Copy)]
pub struct Iface {
desc: Option<&'static TypeDesc>,
data: Data,
}
impl GoValue for Iface {
#[inline]
fn zero() -> Self {
Iface {
desc: None,
data: Data::NONE,
}
}
}
impl Trace for Iface {
#[inline]
fn trace(&self, t: &mut Tracer<'_>) {
self.data.trace(t);
}
}
impl PartialEq for Iface {
fn eq(&self, other: &Self) -> bool {
match (self.desc, other.desc) {
(None, None) => true,
(Some(a), Some(b)) => {
if !core::ptr::eq(a, b) {
return false;
}
match a.equal {
Some(eq) => eq(self.data, other.data),
None => {
crate::panic::runtime_error(crate::panic::RuntimeError::UncomparableType {
type_name: a.name,
})
}
}
}
_ => false,
}
}
}
impl Iface {
#[inline]
pub fn new(desc: &'static TypeDesc, data: Data) -> Self {
Iface {
desc: Some(desc),
data,
}
}
#[inline]
pub fn nil() -> Self {
Iface {
desc: None,
data: Data::NONE,
}
}
pub fn hash_value(self) -> u64 {
let Some(desc) = self.desc else {
return 0;
};
match desc.hash {
Some(h) => crate::map::mix(desc.name.len() as u64, h(self.data)),
None => crate::panic::runtime_error(crate::panic::RuntimeError::UnhashableKey {
type_name: desc.name,
}),
}
}
pub fn desc_addr(self) -> u64 {
self.desc
.map_or(0, |d| d as *const TypeDesc as usize as u64)
}
#[inline]
pub fn is_nil(self) -> bool {
self.desc.is_none()
}
#[inline]
pub fn data(self) -> Data {
self.data
}
pub fn type_name(self) -> &'static str {
self.desc.map_or("nil", |d| d.name)
}
pub fn print_to(self, out: &mut Vec<u8>) {
match self.desc {
Some(d) => (d.print)(self.data, out),
None => out.extend_from_slice(b"<nil>"),
}
}
#[inline]
pub fn method<F: Copy>(self, id: MethodId) -> F {
assert_eq!(
size_of::<F>(),
size_of::<*const ()>(),
"method type must be a fn pointer"
);
let Some(desc) = self.desc else {
crate::panic::runtime_error(crate::panic::RuntimeError::NilDeref)
};
let Some(f) = desc.method(id) else {
unreachable!("method {id} missing from {}", desc.name)
};
unsafe { core::mem::transmute_copy::<*const (), F>(&f.0) }
}
pub fn assert_concrete(self, want: &'static TypeDesc, iface_name: &str) -> Data {
let (data, ok) = self.try_concrete(want);
if !ok {
go_panic(GoPanic::new(match self.desc {
Some(d) => format!(
"interface conversion: {iface_name} is {}, not {}",
d.name, want.name
),
None => format!(
"interface conversion: {iface_name} is nil, not {}",
want.name
),
}));
}
data
}
pub fn try_concrete(self, want: &'static TypeDesc) -> (Data, bool) {
match self.desc {
Some(d) if core::ptr::eq(d, want) => (self.data, true),
_ => (Data::NONE, false),
}
}
pub fn implements(self, ids: &[MethodId]) -> bool {
match self.desc {
Some(d) => ids.iter().all(|id| d.method(*id).is_some()),
None => false,
}
}
pub fn assert_iface(
self,
ids: &[MethodId],
names: &[&str],
want: &str,
iface_name: &str,
) -> Iface {
let Some(desc) = self.desc else {
go_panic(GoPanic::new(format!(
"interface conversion: {iface_name} is nil, not {want}"
)))
};
for (id, name) in ids.iter().zip(names) {
if desc.method(*id).is_none() {
go_panic(GoPanic::new(format!(
"interface conversion: {} is not {want}: missing method {name}",
desc.name
)));
}
}
self
}
}
fn desc_at(p: crate::unsafe_ptr::UPtr) -> &'static TypeDesc {
assert!(p.addr() != 0, "reflection on a nil type");
unsafe { &*(p.addr() as usize as *const TypeDesc) }
}
pub fn desc_comparable(p: crate::unsafe_ptr::UPtr) -> bool {
desc_at(p).equal.is_some()
}
pub fn desc_name(p: crate::unsafe_ptr::UPtr) -> crate::string::GoStr {
crate::string::GoStr::lit(desc_at(p).name.as_bytes())
}