commit fdfa52f4bfd92cd96c8b9e3df91ef562d01207d6 Author: Bert Belder Date: Tue Oct 8 01:56:41 2019 +0200 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..53eaa219 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..6c494892 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "rust.clippy_preference": "on" +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..a44f6039 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "test4" +version = "0.1.0" + diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..d05a2d26 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "test4" +version = "0.1.0" +authors = ["Bert Belder "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/_git2_a11280 b/_git2_a11280 new file mode 120000 index 00000000..9a2c7732 --- /dev/null +++ b/_git2_a11280 @@ -0,0 +1 @@ +testing \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 00000000..a363b483 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,246 @@ +mod channel { + use super::util; + + extern "C" { + // Call a method/destructor; virtual methods use C++ dynamic dispatch. + fn Channel__DTOR(this: &mut Channel) -> (); + fn Channel__a(this: &mut Channel) -> (); + fn Channel__b(this: &Channel) -> i32; + + // Call a method of a specific class implementation, bypassing dynamic + // dispatch. C++ equivalent: `my_channel.Channel::a()`. + fn Channel__Channel__a(this: &mut Channel) -> (); + + // Constructs a special class derived from Channel that forwards all + // virtual method invocations to rust. It is assumed that this subclass + // has the same size and memory layout as the class it's deriving from. + fn Channel__OVERRIDE__CTOR(this: &mut std::mem::MaybeUninit) -> (); + } + + #[repr(C)] + pub struct Channel { + _cxx_vtable: *const [usize; 0], + } + + #[allow(dead_code)] + impl Channel { + pub fn a(&mut self) { + unsafe { Channel__a(self) } + } + pub fn b(&self) -> i32 { + unsafe { Channel__b(self) } + } + } + + pub struct ChannelDefaults; + impl ChannelDefaults { + pub fn a(this: &mut Channel) { + unsafe { Channel__Channel__a(this) } + } + } + + pub trait ChannelOverrides { + fn base(&self) -> &Override; + fn base_mut(&mut self) -> &mut Override; + + fn a(&mut self) { + ChannelDefaults::a(self.base_mut()) + } + fn b(&self) -> i32; + } + + pub struct Override { + cxx_channel: Channel, + base_offset: usize, + rust_vtable: util::RustVTable<&'static dyn ChannelOverrides>, + } + + #[no_mangle] + unsafe extern "C" fn Channel__OVERRIDE__a__DISPATCH(this: &mut Channel) { + Override::dispatch_mut(this).a() + } + #[no_mangle] + unsafe extern "C" fn Channel__OVERRIDE__b__DISPATCH(this: &Channel) -> i32 { + Override::dispatch(this).b() + } + + impl Drop for Channel { + fn drop(&mut self) { + unsafe { Channel__DTOR(self) } + } + } + + impl Override { + fn construct_cxx_channel() -> Channel { + unsafe { + let mut buf = std::mem::MaybeUninit::::uninit(); + Channel__OVERRIDE__CTOR(&mut buf); + buf.assume_init() + } + } + + fn get_base_offset() -> usize + where + T: ChannelOverrides, + { + let buf = std::mem::MaybeUninit::::uninit(); + let top_ptr: *const T = buf.as_ptr(); + let self_ptr: *const Self = unsafe { (*top_ptr).base() }; + util::FieldOffset::from_ptrs(top_ptr, self_ptr).offset() + } + + fn get_rust_vtable() -> util::RustVTable<&'static dyn ChannelOverrides> + where + T: ChannelOverrides, + { + let buf = std::mem::MaybeUninit::::uninit(); + let embedder_ptr = buf.as_ptr(); + let trait_object: *const dyn ChannelOverrides = embedder_ptr; + let (data_ptr, vtable): (*const T, util::RustVTable<_>) = + unsafe { std::mem::transmute(trait_object) }; + assert_eq!(data_ptr, embedder_ptr); + vtable + } + + pub fn new() -> Self + where + T: ChannelOverrides, + { + Self { + cxx_channel: Self::construct_cxx_channel(), + base_offset: Self::get_base_offset::(), + rust_vtable: Self::get_rust_vtable::(), + } + } + + fn channel_offset() -> util::FieldOffset { + let buf = std::mem::MaybeUninit::::uninit(); + util::FieldOffset::from_ptrs(buf.as_ptr(), unsafe { &(*buf.as_ptr()).cxx_channel }) + } + + fn embedder_offset(&self) -> util::FieldOffset { + util::FieldOffset::::from_offset(self.base_offset) + } + + unsafe fn dispatch(channel: &Channel) -> &dyn ChannelOverrides { + let this = Self::channel_offset().to_outer(channel); + let embedder = this.embedder_offset().to_outer(this); + std::mem::transmute((embedder, this.rust_vtable)) + } + + unsafe fn dispatch_mut(channel: &mut Channel) -> &mut dyn ChannelOverrides { + let this = Self::channel_offset().to_outer_mut(channel); + let vtable = this.rust_vtable; + let embedder = this.embedder_offset().to_outer_mut(this); + std::mem::transmute((embedder, vtable)) + } + } + + impl std::ops::Deref for Override { + type Target = Channel; + fn deref(&self) -> &Channel { + &self.cxx_channel + } + } + + impl std::ops::DerefMut for Override { + fn deref_mut(&mut self) -> &mut Channel { + &mut self.cxx_channel + } + } +} + +mod trying { + use super::channel::*; + + #[allow(dead_code)] + pub struct Session { + a: i32, + b: String, + c: Override, + } + + impl ChannelOverrides for Session { + fn base(&self) -> &Override { + &self.c + } + fn base_mut(&mut self) -> &mut Override { + &mut self.c + } + fn a(&mut self) { + println!("Override a!"); + } + fn b(&self) -> i32 { + println!("Override b!"); + 42 + } + } + + impl Session { + pub fn new() -> Self { + Self { + a: 1, + b: "abc".to_owned(), + c: Override::new::(), + } + } + } +} + +mod util { + use std::marker::PhantomData; + use std::mem::size_of; + + pub type Opaque = [usize; 0]; + + #[repr(transparent)] + #[derive(Copy, Clone, Debug)] + pub struct RustVTable(pub *const Opaque, pub PhantomData); + + #[derive(Copy, Clone, Debug)] + #[repr(transparent)] + pub struct FieldOffset(isize, PhantomData<(O, I)>); + + impl FieldOffset { + pub fn from_ptrs(o_ptr: *const O, i_ptr: *const I) -> Self { + let o_addr = o_ptr as usize; + let i_addr = i_ptr as usize; + assert!(i_addr >= o_addr); + assert!((i_addr + size_of::()) <= (o_addr + size_of::())); + let offset = (o_addr - i_addr) as isize; + assert!(offset > 0); + Self(offset, PhantomData) + } + + pub fn from_offset(offset: usize) -> Self { + assert!((offset as isize) > 0); + Self(offset as isize, PhantomData) + } + + pub fn offset(self) -> usize { + self.0 as usize + } + + fn shift(ptr: *const PI, delta: isize) -> *mut PO { + (ptr as isize + delta) as *mut PO + } + pub unsafe fn to_outer<'a>(&self, inner: &'a I) -> &'a O { + Self::shift::(inner, -self.0).as_ref().unwrap() + } + #[allow(dead_code)] + pub unsafe fn to_outer_mut<'a>(&self, inner: &'a mut I) -> &'a mut O { + Self::shift::(inner, -self.0).as_mut().unwrap() + } + } + + impl std::ops::Add> for FieldOffset { + type Output = FieldOffset; + fn add(self, that: FieldOffset) -> Self::Output { + FieldOffset::::from_offset(self.offset() + that.offset()) + } + } +} + +fn main() { + trying::Session::new(); +}