Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Implementing RStruct methods for StableApiDefinition in order to support TruffleRuby #467

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/rb-sys-build/src/bindings/stable_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use quote::ToTokens;

use crate::RbConfig;

const OPAQUE_STRUCTS: [&str; 2] = ["RString", "RArray"];
const OPAQUE_STRUCTS: [&str; 3] = ["RString", "RArray", "RStruct"];

const OPAQUE_STRUCTS_RUBY_3_3: [&str; 3] = [
"rb_matchext_struct",
Expand Down
16 changes: 16 additions & 0 deletions crates/rb-sys-build/src/bindings/wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,19 @@
#ifdef HAVE_RUBY_IO_BUFFER_H
#include "ruby/io/buffer.h"
#endif

struct RStruct {
goyox86 marked this conversation as resolved.
Show resolved Hide resolved
goyox86 marked this conversation as resolved.
Show resolved Hide resolved
struct RBasic basic;
union {
struct {
long len;
const VALUE *ptr;
} heap;
/* This is a length 1 array because:
* 1. GCC has a bug that does not optimize C flexible array members
* (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102452)
* 2. Zero length arrays are not supported by all compilers
*/
const VALUE ary[1];
} as;
};
11 changes: 11 additions & 0 deletions crates/rb-sys-tests/src/stable_api_test.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::ffi::{CStr, CString};

use rb_sys::{StableApiDefinition, VALUE};
use rb_sys_test_helpers::rstring as gen_rstring;

Expand Down Expand Up @@ -655,3 +657,12 @@ parity_test!(
std::time::Duration::from_millis(100)
}
);

parity_test!(
name: test_rb_rstruct_len,
func: rstruct_len,
data_factory: {
ruby_eval!("Person = Struct.new(:name, :age); Person.new('Matz', 59)")
},
expected: 2
);
31 changes: 31 additions & 0 deletions crates/rb-sys/src/stable_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use crate::VALUE;
use std::{
ffi::{c_int, CStr},
os::raw::{c_char, c_long},
ptr::NonNull,
time::Duration,
Expand Down Expand Up @@ -175,6 +176,36 @@ pub trait StableApiDefinition {

/// Blocks the current thread until the given duration has passed.
fn thread_sleep(&self, duration: Duration);

/// Defines a struct type with the given name and members.
///
/// # Example
///
fn rstruct_define(&self, name: &CStr, members: &[&CStr]) -> VALUE;

/// Accesses an indexed member of the struct.
///
/// # Safety
/// This function is unsafe because it dereferences a raw pointer to get
/// access to underlying struct data. The caller must ensure that the
/// `VALUE` is a valid pointer to a struct.
unsafe fn rstruct_get(&self, st: VALUE, idx: c_int) -> VALUE;

/// Sets an indexed member of the struct.
///
/// # Safety
/// This function is unsafe because it dereferences a raw pointer to get
/// access to underlying struct data. The caller must ensure that the
/// `VALUE` is a valid pointer to a struct.
unsafe fn rstruct_set(&self, st: VALUE, idx: c_int, value: VALUE);

/// Returns the number of struct members.
///
/// # Safety
/// This function is unsafe because it dereferences a raw pointer to get
/// access to underlying struct data. The caller must ensure that the
/// `VALUE` is a valid pointer to an RStruct.
unsafe fn rstruct_len(&self, obj: VALUE) -> c_long;
}

#[cfg(stable_api_enable_compiled_mod)]
Expand Down
20 changes: 20 additions & 0 deletions crates/rb-sys/src/stable_api/compiled.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,23 @@ impl_thread_sleep(struct timeval time) {
rb_thread_wait_for(time);
}

long
impl_rstruct_len(VALUE st) {
return RSTRUCT_LEN(st);
}

VALUE
impl_rstruct_define(char* name, ...) {
return rb_struct_define(name, NULL);
}

VALUE
impl_rstruct_get(VALUE st, int idx) {
return Qnil;
}

VALUE
impl_rstruct_set(VALUE st, int idx, VALUE value) {
return Qnil;
}

39 changes: 39 additions & 0 deletions crates/rb-sys/src/stable_api/compiled.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::StableApiDefinition;
use crate::{ruby_value_type, timeval, VALUE};
use std::{
ffi::{c_int, CStr},
os::raw::{c_char, c_long},
ptr::NonNull,
time::Duration,
Expand Down Expand Up @@ -79,6 +80,18 @@ extern "C" {

#[link_name = "impl_thread_sleep"]
fn impl_thread_sleep(interval: timeval);

#[link_name = "impl_rstruct_define"]
fn impl_rstruct_define(name: &CStr, members: Vec<*const c_char>) -> VALUE;

#[link_name = "impl_rstruct_get"]
fn impl_rstruct_get(st: VALUE, idx: c_int) -> VALUE;

#[link_name = "impl_rstruct_set"]
fn impl_rstruct_set(st: VALUE, idx: c_int, value: VALUE);

#[link_name = "impl_rstruct_len"]
fn impl_rstruct_len(obj: VALUE) -> c_long;
}

pub struct Definition;
Expand Down Expand Up @@ -112,6 +125,7 @@ impl StableApiDefinition for Definition {
NonNull::<VALUE>::new(impl_rbasic_class(obj) as _)
}

#[inline]
unsafe fn frozen_p(&self, obj: VALUE) -> bool {
impl_frozen_p(obj)
}
Expand Down Expand Up @@ -213,4 +227,29 @@ impl StableApiDefinition for Definition {

unsafe { impl_thread_sleep(time) }
}

#[inline]
fn rstruct_define(&self, name: &CStr, members: &[&CStr]) -> VALUE {
let mut members: Vec<*const c_char> = members
.iter()
.map(|m| m.as_ptr() as *const c_char)
.collect();
members.push(std::ptr::null());
unsafe { impl_rstruct_define(name, members) }
}

#[inline]
unsafe fn rstruct_get(&self, st: VALUE, idx: c_int) -> VALUE {
impl_rstruct_get(st, idx)
}

#[inline]
unsafe fn rstruct_set(&self, st: VALUE, idx: c_int, value: VALUE) {
impl_rstruct_set(st, idx, value)
}

#[inline]
unsafe fn rstruct_len(&self, st: VALUE) -> c_long {
impl_rstruct_len(st)
}
}
46 changes: 45 additions & 1 deletion crates/rb-sys/src/stable_api/ruby_3_3.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
use super::StableApiDefinition;
use crate::{
internal::{RArray, RString},
value_type, VALUE,
ruby_fl_type, value_type, VALUE,
};
use std::{
ffi::{c_int, CStr},
os::raw::{c_char, c_long},
ptr::NonNull,
time::Duration,
};

const RSTRUCT_EMBED_LEN_MASK: i64 = (ruby_fl_type::RUBY_FL_USER7 as i64)
goyox86 marked this conversation as resolved.
Show resolved Hide resolved
| (ruby_fl_type::RUBY_FL_USER6 as i64)
| (ruby_fl_type::RUBY_FL_USER5 as i64)
| (ruby_fl_type::RUBY_FL_USER4 as i64)
| (ruby_fl_type::RUBY_FL_USER3 as i64)
| (ruby_fl_type::RUBY_FL_USER2 as i64)
| (ruby_fl_type::RUBY_FL_USER1 as i64);

const RSTRUCT_EMBED_LEN_SHIFT: i64 = (crate::ruby_fl_ushift::RUBY_FL_USHIFT as i64) + 1;

#[cfg(not(ruby_eq_3_3))]
compile_error!("This file should only be included in Ruby 3.3 builds");

Expand Down Expand Up @@ -268,4 +279,37 @@ impl StableApiDefinition for Definition {

unsafe { crate::rb_thread_wait_for(time) }
}

fn rstruct_define(&self, name: &CStr, members: &[&CStr]) -> VALUE {
let mut members: Vec<*const c_char> = members
.iter()
.map(|m| m.as_ptr() as *const c_char)
.collect();
members.push(std::ptr::null());
unsafe { crate::rb_struct_define(name.as_ptr(), members) }
}

unsafe fn rstruct_get(&self, st: VALUE, idx: c_int) -> VALUE {
crate::rb_struct_getmember(st, idx as _)
}

unsafe fn rstruct_set(&self, st: VALUE, idx: c_int, value: VALUE) {
crate::rb_struct_aset(st, idx as _, value);
}

// unsafe fn rstruct_len(&self, st: VALUE) -> c_long {
// crate::rb_num2long(crate::rb_struct_size(st))
// }

unsafe fn rstruct_len(&self, st: VALUE) -> c_long {
let rbasic = st as *const crate::RBasic;
if ((*rbasic).flags & RSTRUCT_EMBED_LEN_MASK as VALUE) != 0 {
let mut ret = ((*rbasic).flags & RSTRUCT_EMBED_LEN_MASK as VALUE) as c_long;
ret >>= RSTRUCT_EMBED_LEN_SHIFT;
ret
} else {
let rstruct = st as *const crate::RStruct;
goyox86 marked this conversation as resolved.
Show resolved Hide resolved
(*rstruct).as_.heap.len as c_long
}
}
}
Loading