mruby_compiler2_sys/
lib.rs1use std::ptr::null_mut;
2
3mod bindings {
4 #![allow(nonstandard_style)]
5 #![allow(unused)]
6 #![allow(unnecessary_transmutes)]
7 include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
8}
9use bindings::{
10 MRC_DUMP_OK, mrc_ccontext, mrc_ccontext_free, mrc_ccontext_new, mrc_dump_irep, mrc_irep,
11 mrc_irep_free, mrc_load_string_cxt,
12};
13
14#[cfg(feature = "std")]
15use std::os::unix::io::AsRawFd;
16
17#[cfg(feature = "std")]
18use bindings::{FILE, fdopen, mrc_codedump_all, mrc_dump_irep_cfunc};
19
20#[derive(Debug)]
21pub struct MRubyCompiler2Error {
22 details: String,
23}
24
25impl MRubyCompiler2Error {
26 fn new(msg: &str) -> MRubyCompiler2Error {
27 MRubyCompiler2Error {
28 details: msg.to_string(),
29 }
30 }
31
32 #[allow(unused)]
33 fn from_error<E: std::error::Error>(msg: &str, err: E) -> MRubyCompiler2Error {
34 MRubyCompiler2Error {
35 details: format!("{}: {}", msg, err.to_string()),
36 }
37 }
38}
39
40impl std::fmt::Display for MRubyCompiler2Error {
41 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
42 write!(f, "{}", self.details)
43 }
44}
45
46impl std::error::Error for MRubyCompiler2Error {}
47
48pub struct MRubyCompiler2Context {
49 c: *mut mrc_ccontext,
50}
51
52impl MRubyCompiler2Context {
53 pub unsafe fn new() -> Self {
55 unsafe {
56 let ccontext = mrc_ccontext_new(null_mut());
57 MRubyCompiler2Context { c: ccontext }
58 }
59 }
60
61 pub unsafe fn compile(&mut self, code: &str) -> Result<Vec<u8>, MRubyCompiler2Error> {
64 unsafe {
65 let c_code = std::ffi::CString::new(code)
66 .map_err(|_| MRubyCompiler2Error::new("Code includes null bytes"))?;
67 let mut ptr = c_code.as_ptr() as *const u8;
68 let irep =
69 mrc_load_string_cxt(self.c, &mut ptr as *mut *const u8, c_code.as_bytes().len());
70
71 if irep.is_null() {
72 return Err(MRubyCompiler2Error::new("Failed to compile code"));
73 }
74
75 let bin: &'static mut [u8] = Vec::with_capacity(code.len() * 2).leak();
78 let bin_ptr = bin.as_mut_ptr();
79 let mut bin_size: usize = 0;
80
81 let result = mrc_dump_irep(
82 self.c,
83 irep as *mut mrc_irep,
84 0,
85 &bin_ptr as *const *mut u8 as *mut *mut u8,
86 &mut bin_size as *mut usize,
87 );
88 mrc_irep_free(self.c, irep as *mut mrc_irep);
89 if result as u32 != MRC_DUMP_OK {
90 return Err(MRubyCompiler2Error::new("Failed to dump irep binary"));
91 }
92
93 let newvec = Vec::from_raw_parts(bin_ptr, bin_size, bin_size);
94 Ok(newvec)
95 }
96 }
97
98 #[cfg(feature = "std")]
100 pub unsafe fn dump_bytecode(&mut self, code: &str) -> Result<(), MRubyCompiler2Error> {
101 unsafe {
102 let c_code = std::ffi::CString::new(code)
103 .map_err(|_| MRubyCompiler2Error::new("Code includes null bytes"))?;
104 let mut ptr = c_code.as_ptr() as *const u8;
105 let irep =
106 mrc_load_string_cxt(self.c, &mut ptr as *mut *const u8, c_code.as_bytes().len());
107
108 if irep.is_null() {
109 return Err(MRubyCompiler2Error::new("Failed to compile code"));
110 }
111
112 mrc_codedump_all(self.c, irep as *mut mrc_irep);
113 mrc_irep_free(self.c, irep as *mut mrc_irep);
114 Ok(())
115 }
116 }
117
118 #[cfg(feature = "std")]
120 pub unsafe fn compile_to_file(
121 &mut self,
122 code: &str,
123 path: &std::path::Path,
124 ) -> Result<(), Box<dyn std::error::Error>> {
125 let bin = unsafe { self.compile(code) }?;
126 let mut out = std::fs::File::create(path)?;
127 std::io::Write::write_all(&mut out, &bin)?;
128 Ok(())
129 }
130
131 #[cfg(feature = "std")]
133 pub unsafe fn compile_to_c_function(
134 &mut self,
135 code: &str,
136 initname: &str,
137 path: &std::path::Path,
138 ) -> Result<(), MRubyCompiler2Error> {
139 let out = std::fs::File::create(path)
140 .map_err(|e| MRubyCompiler2Error::from_error("Failed to create file", e))?;
141
142 unsafe {
143 let c_code = std::ffi::CString::new(code)
144 .map_err(|e| MRubyCompiler2Error::from_error("Code includes null bytes", e))?;
145 let mut ptr = c_code.as_ptr() as *const u8;
146 let irep =
147 mrc_load_string_cxt(self.c, &mut ptr as *mut *const u8, c_code.as_bytes().len());
148
149 if irep.is_null() {
150 return Err(MRubyCompiler2Error::new("Failed to compile code"));
151 }
152 let fd = out.as_raw_fd();
153 let mode_str = std::ffi::CString::new("w").unwrap();
154 let fp = fdopen(fd, mode_str.as_ptr());
155 std::mem::forget(out);
156
157 let initname = std::ffi::CString::new(initname)
158 .map_err(|e| MRubyCompiler2Error::from_error("Initname includes null bytes", e))?;
159
160 let result = mrc_dump_irep_cfunc(self.c, irep, 0, fp as *mut FILE, initname.as_ptr());
161 mrc_irep_free(self.c, irep as *mut mrc_irep);
162 if result as u32 != MRC_DUMP_OK {
163 return Err(MRubyCompiler2Error::new("Failed to dump irep binary"));
164 }
165 Ok(())
166 }
167 }
168}
169
170impl Drop for MRubyCompiler2Context {
171 fn drop(&mut self) {
172 unsafe {
173 mrc_ccontext_free(self.c);
174 }
175 }
176}