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
9 #[cfg(target_arch = "wasm32")]
10 unsafe extern "C" {
11 pub fn mrc_ccontext_new(mrb: *mut ::std::os::raw::c_void) -> *mut mrc_ccontext;
12 pub fn mrc_ccontext_free(c: *mut mrc_ccontext);
13 pub fn mrc_load_string_cxt(
14 c: *mut mrc_ccontext,
15 source: *mut *const u8,
16 length: usize,
17 ) -> *mut mrc_irep;
18 pub fn mrc_dump_irep(
19 c: *mut mrc_ccontext,
20 irep: *const mrc_irep,
21 flags: u8,
22 bin: *mut *mut u8,
23 bin_size: *mut usize,
24 ) -> ::std::os::raw::c_int;
25 pub fn mrc_irep_free(c: *mut mrc_ccontext, irep: *mut mrc_irep);
26 }
27
28 #[cfg(all(target_arch = "wasm32", feature = "std"))]
29 unsafe extern "C" {
30 pub fn fdopen(
31 arg1: ::std::os::raw::c_int,
32 arg2: *const ::std::os::raw::c_char,
33 ) -> *mut FILE;
34 pub fn mrc_codedump_all(c: *mut mrc_ccontext, irep: *mut mrc_irep);
35 pub fn mrc_dump_irep_cfunc(
36 c: *mut mrc_ccontext,
37 irep: *const mrc_irep,
38 flags: u8,
39 fp: *mut FILE,
40 initname: *const ::std::os::raw::c_char,
41 ) -> ::std::os::raw::c_int;
42 }
43}
44use bindings::{
45 MRC_DUMP_OK, mrc_ccontext, mrc_ccontext_free, mrc_ccontext_new, mrc_dump_irep, mrc_irep,
46 mrc_irep_free, mrc_load_string_cxt,
47};
48
49#[cfg(feature = "std")]
50use std::os::unix::io::AsRawFd;
51
52#[cfg(feature = "std")]
53use bindings::{FILE, fdopen, mrc_codedump_all, mrc_dump_irep_cfunc};
54
55#[derive(Debug)]
56pub struct MRubyCompiler2Error {
57 details: String,
58}
59
60impl MRubyCompiler2Error {
61 fn new(msg: &str) -> MRubyCompiler2Error {
62 MRubyCompiler2Error {
63 details: msg.to_string(),
64 }
65 }
66
67 #[allow(unused)]
68 fn from_error<E: std::error::Error>(msg: &str, err: E) -> MRubyCompiler2Error {
69 MRubyCompiler2Error {
70 details: format!("{}: {}", msg, err.to_string()),
71 }
72 }
73}
74
75impl std::fmt::Display for MRubyCompiler2Error {
76 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
77 write!(f, "{}", self.details)
78 }
79}
80
81impl std::error::Error for MRubyCompiler2Error {}
82
83pub struct MRubyCompiler2Context {
84 c: *mut mrc_ccontext,
85}
86
87impl MRubyCompiler2Context {
88 pub unsafe fn new() -> Self {
90 unsafe {
91 let ccontext = mrc_ccontext_new(null_mut());
92 MRubyCompiler2Context { c: ccontext }
93 }
94 }
95
96 pub unsafe fn compile(&mut self, code: &str) -> Result<Vec<u8>, MRubyCompiler2Error> {
99 unsafe {
100 let c_code = std::ffi::CString::new(code)
101 .map_err(|_| MRubyCompiler2Error::new("Code includes null bytes"))?;
102 let mut ptr = c_code.as_ptr() as *const u8;
103 let irep =
104 mrc_load_string_cxt(self.c, &mut ptr as *mut *const u8, c_code.as_bytes().len());
105
106 if irep.is_null() {
107 return Err(MRubyCompiler2Error::new("Failed to compile code"));
108 }
109
110 let bin: &'static mut [u8] = Vec::with_capacity(code.len() * 2).leak();
113 let bin_ptr = bin.as_mut_ptr();
114 let mut bin_size: usize = 0;
115
116 let result = mrc_dump_irep(
117 self.c,
118 irep as *mut mrc_irep,
119 0,
120 &bin_ptr as *const *mut u8 as *mut *mut u8,
121 &mut bin_size as *mut usize,
122 );
123 mrc_irep_free(self.c, irep as *mut mrc_irep);
124 if result as u32 != MRC_DUMP_OK {
125 return Err(MRubyCompiler2Error::new("Failed to dump irep binary"));
126 }
127
128 let newvec = Vec::from_raw_parts(bin_ptr, bin_size, bin_size);
129 Ok(newvec)
130 }
131 }
132
133 #[cfg(feature = "std")]
135 pub unsafe fn dump_bytecode(&mut self, code: &str) -> Result<(), MRubyCompiler2Error> {
136 unsafe {
137 let c_code = std::ffi::CString::new(code)
138 .map_err(|_| MRubyCompiler2Error::new("Code includes null bytes"))?;
139 let mut ptr = c_code.as_ptr() as *const u8;
140 let irep =
141 mrc_load_string_cxt(self.c, &mut ptr as *mut *const u8, c_code.as_bytes().len());
142
143 if irep.is_null() {
144 return Err(MRubyCompiler2Error::new("Failed to compile code"));
145 }
146
147 mrc_codedump_all(self.c, irep as *mut mrc_irep);
148 mrc_irep_free(self.c, irep as *mut mrc_irep);
149 Ok(())
150 }
151 }
152
153 #[cfg(feature = "std")]
155 pub unsafe fn compile_to_file(
156 &mut self,
157 code: &str,
158 path: &std::path::Path,
159 ) -> Result<(), Box<dyn std::error::Error>> {
160 let bin = unsafe { self.compile(code) }?;
161 let mut out = std::fs::File::create(path)?;
162 std::io::Write::write_all(&mut out, &bin)?;
163 Ok(())
164 }
165
166 #[cfg(feature = "std")]
168 pub unsafe fn compile_to_c_function(
169 &mut self,
170 code: &str,
171 initname: &str,
172 path: &std::path::Path,
173 ) -> Result<(), MRubyCompiler2Error> {
174 let out = std::fs::File::create(path)
175 .map_err(|e| MRubyCompiler2Error::from_error("Failed to create file", e))?;
176
177 unsafe {
178 let c_code = std::ffi::CString::new(code)
179 .map_err(|e| MRubyCompiler2Error::from_error("Code includes null bytes", e))?;
180 let mut ptr = c_code.as_ptr() as *const u8;
181 let irep =
182 mrc_load_string_cxt(self.c, &mut ptr as *mut *const u8, c_code.as_bytes().len());
183
184 if irep.is_null() {
185 return Err(MRubyCompiler2Error::new("Failed to compile code"));
186 }
187 let fd = out.as_raw_fd();
188 let mode_str = std::ffi::CString::new("w").unwrap();
189 let fp = fdopen(fd, mode_str.as_ptr());
190 std::mem::forget(out);
191
192 let initname = std::ffi::CString::new(initname)
193 .map_err(|e| MRubyCompiler2Error::from_error("Initname includes null bytes", e))?;
194
195 let result = mrc_dump_irep_cfunc(self.c, irep, 0, fp as *mut FILE, initname.as_ptr());
196 mrc_irep_free(self.c, irep as *mut mrc_irep);
197 if result as u32 != MRC_DUMP_OK {
198 return Err(MRubyCompiler2Error::new("Failed to dump irep binary"));
199 }
200 Ok(())
201 }
202 }
203}
204
205impl Drop for MRubyCompiler2Context {
206 fn drop(&mut self) {
207 unsafe {
208 mrc_ccontext_free(self.c);
209 }
210 }
211}