1pub use self::protect::{catch_unwind, CaughtException};
11pub use self::value::Value;
12
13#[derive(Copy, Clone, Debug)]
17pub struct Binding(pub Value);
18
19impl Binding {
20 pub fn top_level() -> Self {
24 Binding(unsafe { std::classes::Object().constant_unprotected("TOPLEVEL_BINDING") })
25 }
26
27 pub fn allocate() -> Self {
29 unsafe { Binding(std::modules::Kernel().send_unprotected("binding", &[])) }
30 }
31}
32
33pub fn eval(
38 ruby_code: &str,
39 binding: Binding,
40 filename_for_debugging: Option<&str>,
41) -> Result<Value, CaughtException> {
42 crate::helpers::catch_unwind(|| unsafe {
43 eval_unprotected(ruby_code, binding, filename_for_debugging)
44 })
45}
46
47pub unsafe fn eval_unprotected(
50 ruby_code: &str,
51 binding: Binding,
52 filename_for_debugging: Option<&str>,
53) -> Value {
54 let code_string = crate::helpers::to_ruby::string(ruby_code);
55 let filename = filename_for_debugging.map(crate::helpers::to_ruby::string);
56 let mut argv = vec![
59 *code_string,
60 ];
61
62 if let Some(filename) = filename { argv.push(*filename) };
63 binding.0.send_unprotected("eval", &argv[..])
66}
67
68pub mod to_ruby {
70 use super::Value;
71
72 pub struct WrappedWithData<T, D> {
74 value: T,
75 _data: D,
76 }
77
78 pub fn string(string: &str)
80 -> WrappedWithData<Value, std::ffi::CString> {
81 let cstring = std::ffi::CString::new(string).unwrap();
82 let string_as_value = unsafe {
83 crate::rb_str_new_cstr(cstring.as_ptr())
84 };
85
86 WrappedWithData {
87 _data: cstring,
88 value: Value(string_as_value),
89 }
90 }
91
92 pub fn symbol(string: &str)
94 -> WrappedWithData<crate::ID, std::ffi::CString> {
95 let cstring = std::ffi::CString::new(string).unwrap();
96 let string_as_value = unsafe {
97 crate::rb_intern(cstring.as_ptr())
98 };
99
100 WrappedWithData {
101 _data: cstring,
102 value: string_as_value,
103 }
104 }
105
106 impl<T, D> AsRef<T> for WrappedWithData<T, D> {
107 fn as_ref(&self) -> &T { &self.value }
108 }
109
110 impl<T, D> std::ops::Deref for WrappedWithData<T, D> {
111 type Target = T;
112
113 fn deref(&self) -> &T { &self.value }
114 }
115}
116
117mod protect {
119 use super::{to_ruby, Value};
120 use crate::VALUE;
121
122 #[derive(Debug)]
124 pub struct CaughtException {
125 pub exception_object: Value,
126 pub exception_class_name: String,
127 pub message: String,
128 }
129
130 impl PartialEq for CaughtException {
132 fn eq(&self, rhs: &Self) -> bool {
133 let CaughtException {
134 ref exception_class_name, ref message,
135 exception_object: _,
136 } = *self;
137
138 *exception_class_name == rhs.exception_class_name &&
139 *message == rhs.message
140 }
141 }
142
143 impl Eq for CaughtException { }
144
145 pub fn catch_unwind<F>(
151 mut f: F,
152 ) -> Result<Value, CaughtException>
153 where F: FnOnce() -> Value {
154 let mut state: libc::c_int = 1;
155
156 let mut fn_ptr_buf: *mut F = &mut f;
160
161 let fn_ptr_buf_ref: &mut *mut F = &mut fn_ptr_buf;
162 let fn_ptr_buf_ptr: *mut *mut F = fn_ptr_buf_ref as _;
163 let fn_ptr_buf_ptr_uint = fn_ptr_buf_ptr as usize; let fn_ptr_buf_ptr_as_ruby_string = to_ruby::string(&fn_ptr_buf_ptr_uint.to_string());
166
167 let catch_unwind_internal_args = (*fn_ptr_buf_ptr_as_ruby_string).0;
168
169 let result = unsafe {
170 crate::rb_protect(catch_unwind_internal::<F>, catch_unwind_internal_args, &mut state)
171 };
172
173 if state == 0 {
174 Ok(Value(result))
175 } else {
176 let exception_object: Value = Value(unsafe { crate::rb_errinfo() });
177 unsafe { crate::rb_set_errinfo(crate::Qnil) }; let message = unsafe { exception_object.send_unprotected("message", &[]).to_s_unprotected() };
180 let class_name = exception_object.object_class_name();
181
182 Err(CaughtException {
183 exception_object,
184 exception_class_name: class_name,
185 message,
186 })
187 }
188 }
189
190 extern "C" fn catch_unwind_internal<F>(
191 fn_ptr_as_ruby_string: VALUE,
192 ) -> VALUE
193 where F: FnOnce() -> Value {
194 let fn_ptr_buf_ptr_as_rust_string: String = unsafe {
195 Value(fn_ptr_as_ruby_string).to_s_unprotected()
196 };
197
198 let fn_ptr_buf_ptr_uint: usize = match fn_ptr_buf_ptr_as_rust_string.parse() {
199 Ok(uint) => uint,
200 Err(..) => {
201 eprintln!("this should never happen, choking on our own string");
202 std::process::abort();
203 },
204 };
205 let fn_ptr_buf_ptr: *mut *mut F = fn_ptr_buf_ptr_uint as _;
206 let fn_ptr_buf_ref: &mut *mut F = unsafe { std::mem::transmute(fn_ptr_buf_ptr) };
207
208 let fn_ptr: *mut F = std::mem::replace(fn_ptr_buf_ref, std::ptr::null_mut());
209 let fn_ref: &mut F = unsafe { std::mem::transmute(fn_ptr) };
210 let f = std::mem::replace(fn_ref, unsafe { std::mem::MaybeUninit::zeroed().assume_init() });
211
212 (f)().0
213 }
214
215 impl std::fmt::Display for CaughtException {
217 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
218 write!(fmt, "{}: {}", self.exception_class_name, self.message)
219 }
220 }
221
222 impl std::error::Error for CaughtException { }
223}
224
225mod value {
226 use super::{to_ruby, CaughtException};
227 use crate::VALUE;
228
229 #[derive(Copy, Clone, PartialEq, Eq)]
231 #[repr(transparent)]
232 pub struct Value(pub VALUE);
233
234 impl Value {
235 pub const NIL: Self = Value(crate::Qnil);
237 pub const TRUE: Self = Value(crate::Qtrue);
239 pub const FALSE: Self = Value(crate::Qfalse);
241
242 pub fn send(
244 &self,
245 method_name: &str,
246 arguments: &[Value],
247 ) -> Result<Value, CaughtException> {
248 crate::helpers::catch_unwind(|| unsafe {
249 self.send_unprotected(method_name, arguments)
250 })
251 }
252
253 pub unsafe fn send_unprotected(
255 &self,
256 method_name: &str,
257 arguments: &[Value],
258 ) -> Value {
259 let function_symbol = to_ruby::symbol(method_name);
260 let arguments = Value::convert_array(arguments);
261 Value(crate::rb_funcallv(self.0, *function_symbol, arguments.len() as _, arguments.as_ptr()))
262 }
263
264 pub fn constant(
266 &self,
267 constant_name: &str,
268 ) -> Result<Value, CaughtException> {
269 crate::helpers::catch_unwind(|| unsafe { self.constant_unprotected(constant_name) })
270 }
271
272 pub unsafe fn constant_unprotected(
274 &self,
275 constant_name: &str,
276 ) -> Value {
277 let constant_symbol = to_ruby::symbol(constant_name);
278 Value(crate::rb_const_get(self.0, *constant_symbol) )
279 }
280
281 pub fn set_constant(
283 &self,
284 constant_name: &str,
285 value: Value,
286 ) -> Result<(), CaughtException> {
287 crate::helpers::catch_unwind(|| unsafe {
288 self.set_constant_unprotected(constant_name, value);
289
290 Value::NIL
291 }).map(|_| ())
292 }
293
294 pub unsafe fn set_constant_unprotected(
296 &self,
297 constant_name: &str,
298 value: Value,
299 ) {
300 let constant_symbol = to_ruby::symbol(constant_name);
301 crate::rb_const_set(self.0, *constant_symbol, value.0)
302 }
303
304 pub fn to_s(&self) -> Result<String, CaughtException> {
308 super::catch_unwind(|| unsafe {
309 self.send_unprotected("to_s", &[])
310 }).map(|v| unsafe { v.assert_is_string_and_convert_to_string_unprotected() })
311 }
312
313 pub unsafe fn to_s_unprotected(&self) -> String {
315 self.send_unprotected("to_s", &[])
316 .assert_is_string_and_convert_to_string_unprotected()
317 }
318
319 unsafe fn assert_is_string_and_convert_to_string_unprotected(&self) -> String {
320 let cstring_ptr = crate::rb_string_value_cstr(&self.0);
321 let cstr = std::ffi::CStr::from_ptr(cstring_ptr);
322
323 cstr.to_str().expect("invalid UTF-8").to_owned()
324 }
325
326 pub fn object_class_name(&self) -> String {
329 unsafe {
330 let cstr_ptr = crate::rb_obj_classname(self.0);
331 std::ffi::CStr::from_ptr(cstr_ptr).to_str().unwrap().to_owned()
332 }
333 }
334
335 pub fn inspect(&self) -> Result<Value, CaughtException> {
337 super::catch_unwind(|| unsafe { self.inspect_unprotected() })
338 }
339
340 pub unsafe fn inspect_unprotected(&self) -> Value {
342 self.send_unprotected("inspect", &[])
343 }
344
345 pub fn is_of_value_type(&self, value_type: crate::value_type) -> bool {
347 crate::TYPE_P(self.0, value_type)
348 }
349
350 pub fn is_nil(&self) -> bool { self.0 == crate::Qnil }
352
353
354 pub fn convert_array(values: &[Value]) -> &[VALUE] {
355 unsafe { std::mem::transmute(values) } }
357 }
358
359 impl std::fmt::Display for Value {
360 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
361 self.to_s().unwrap_or_else(|e| format!("ERROR: unexpected ruby exception: {}", e)).fmt(fmt)
362 }
363 }
364
365 impl std::fmt::Debug for Value {
366 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
367 super::catch_unwind(|| unsafe {
368 let inspect_string = self.inspect_unprotected().to_s_unprotected();
369 write!(fmt, "{}", inspect_string).ok();
370
371 Value::NIL
372 }).expect("Ruby method #inspect failed");
373
374 Ok(())
375 }
376 }
377
378 impl std::str::FromStr for Value {
379 type Err = CaughtException;
380
381 fn from_str(s: &str) -> Result<Self, CaughtException> {
382 Ok(*to_ruby::string(s))
383 }
384 }
385
386 impl From<bool> for Value {
387 fn from(b: bool) -> Self {
388 if b { Value::TRUE } else { Value::FALSE }
389 }
390 }
391
392 impl From<VALUE> for Value {
393 fn from(v: VALUE) -> Self { Value(v) }
394 }
395
396 impl Into<VALUE> for Value {
397 fn into(self) -> VALUE { self.0 }
398 }
399
400 impl AsRef<VALUE> for Value {
401 fn as_ref(&self) -> &VALUE { &self.0 }
402 }
403
404 impl std::ops::Deref for Value {
405 type Target = VALUE;
406
407 fn deref(&self) -> &VALUE { &self.0 }
408 }
409
410 impl std::ops::DerefMut for Value {
411 fn deref_mut(&mut self) -> &mut VALUE { &mut self.0 }
412 }
413}
414
415pub mod std {
418 #![allow(non_snake_case)]
419
420 pub mod modules {
421 use super::super::Value;
422
423 pub fn Kernel() -> Value { Value(unsafe { crate::rb_mKernel }) }
424 pub fn Math() -> Value { Value(unsafe { crate::rb_mMath }) }
425 }
426
427 pub mod classes {
428 use super::super::Value;
429
430 pub fn Object() -> Value { Value(unsafe { crate::rb_cObject}) }
431 pub fn Array() -> Value { Value(unsafe { crate::rb_cArray}) }
432 pub fn Binding() -> Value { Value(unsafe { crate::rb_cBinding}) }
433 pub fn Class() -> Value { Value(unsafe { crate::rb_cClass}) }
434 pub fn Module() -> Value { Value(unsafe { crate::rb_cModule}) }
435 pub fn NilClass() -> Value { Value(unsafe { crate::rb_cNilClass}) }
436 pub fn Integer() -> Value { Value(unsafe { crate::rb_cInteger}) }
437 pub fn Hash() -> Value { Value(unsafe { crate::rb_cHash}) }
438 pub fn Float() -> Value { Value(unsafe { crate::rb_cFloat}) }
439 }
440}