Skip to content

Commit 855a5cd

Browse files
authored
Merge pull request #2 from udzura/api-impl
Implement basic dump API
2 parents bfdb696 + d5c392c commit 855a5cd

9 files changed

Lines changed: 172 additions & 32 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
/target
22

33
*.mrb
4+
examples/out.c

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,14 @@ include = [
1313
]
1414

1515
[dependencies]
16+
libc = { version = "0.2.177", optional = true }
1617

1718
[build-dependencies]
1819
bindgen = ">= 0"
1920
cc = ">= 1"
20-
glob = ">= 0"
21+
glob = ">= 0"
22+
23+
[features]
24+
default = [ "std" ]
25+
std = [ "dep:libc" ]
26+
no-std = []

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# mruby-compiler2-sys
2+
Naive mruby-compiler2 binding for Rust
3+
4+
See Rust doc [mruby-compiler2-sys](https://crates.io/crates/mruby-compiler2-sys) for details.
5+
6+
## License
7+
See LICENSE file.
8+
9+
NOTE: This crate includes code from mruby-compiler2 and prism, which are under the MIT License.

build.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,8 @@ fn main() {
3030

3131
println!("cargo:rustc-link-lib=mrubycompiler2");
3232
let bindings = bindgen::Builder::default()
33-
.header("./vendor/mruby-compiler2/include/mrc_ccontext.h")
34-
.header("./vendor/mruby-compiler2/include/mrc_irep.h")
35-
.header("./vendor/mruby-compiler2/include/mrc_compile.h")
36-
.header("./vendor/mruby-compiler2/include/mrc_dump.h")
33+
.header("./vendor/mruby-compiler2/include/mruby_compiler.h")
34+
.header("./vendor/mruby-compiler2/include/mrc_codedump.h")
3735
.clang_arg("-I./vendor/mruby-compiler2/include")
3836
.clang_arg("-I./vendor/mruby-compiler2/lib/prism/include")
3937
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))

examples/cfunc.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
extern crate mruby_compiler2_sys;
2+
use mruby_compiler2_sys::MRubyCompiler2Context;
3+
4+
fn main() -> Result<(), Box<dyn std::error::Error>> {
5+
unsafe {
6+
let mut cxt = MRubyCompiler2Context::new();
7+
cxt.compile_to_c_function(
8+
"puts \"Hello, mruby-compiler2!\"",
9+
"init_test_func",
10+
std::path::Path::new("examples/out.c"),
11+
)?;
12+
}
13+
println!("Created examples/out.c");
14+
Ok(())
15+
}

examples/smoke.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
extern crate mruby_compiler2_sys;
22
use mruby_compiler2_sys::MRubyCompiler2Context;
33

4-
fn main() {
4+
fn main() -> Result<(), Box<dyn std::error::Error>> {
55
unsafe {
66
let mut cxt = MRubyCompiler2Context::new();
7-
let bin = cxt.compile("puts \"Hello, mruby-compiler2!\"");
8-
println!("Compiled bytecode size: {}", bin.len());
7+
cxt.dump_bytecode("puts \"Hello, mruby-compiler2!\"")?;
98

10-
let out = std::fs::File::create("examples/out.mrb").unwrap();
11-
std::io::Write::write_all(&mut &out, &bin).unwrap();
9+
let bin = cxt.compile("puts \"Hello, mruby-compiler2!\"")?;
10+
11+
let out = std::fs::File::create("examples/out.mrb")?;
12+
std::io::Write::write_all(&mut &out, &bin)?;
13+
14+
println!("Compiled bytecode file: examples/out.mrb, size: {}", bin.len());
1215
}
16+
Ok(())
1317
}

src/bindings.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17260,6 +17260,12 @@ const _: () = {
1726017260
["Offset of field: rite_binary_footer::section_size"]
1726117261
[::std::mem::offset_of!(rite_binary_footer, section_size) - 4usize];
1726217262
};
17263+
unsafe extern "C" {
17264+
pub fn mrc_codedump_all_file(c: *mut mrc_ccontext, irep: *mut mrc_irep, out: *mut FILE);
17265+
}
17266+
unsafe extern "C" {
17267+
pub fn mrc_codedump_all(c: *mut mrc_ccontext, irep: *mut mrc_irep);
17268+
}
1726317269
pub type __builtin_va_list = *mut ::std::os::raw::c_char;
1726417270
pub type __uint128_t = u128;
1726517271
#[repr(C)]

src/lib.rs

Lines changed: 122 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,44 +7,74 @@ mod bindings {
77
include!("./bindings.rs");
88
}
99
use bindings::{
10-
mrc_ccontext,
11-
mrc_ccontext_new,
12-
mrc_load_string_cxt,
13-
mrc_irep,
14-
mrc_dump_irep,
15-
MRC_DUMP_OK,
16-
mrc_irep_free,
17-
mrc_ccontext_free,
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,
1812
};
1913

14+
#[cfg(feature = "std")]
15+
use std::os::unix::io::AsRawFd;
16+
17+
#[cfg(feature = "std")]
18+
use bindings::{FILE, fdopen, mrc_codedump_all, mrc_dump_irep_cfunc};
19+
20+
#[derive(Debug)]
21+
pub struct MRubyCompiler2Error {
22+
details: String,
23+
}
24+
25+
impl 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+
40+
impl 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+
46+
impl std::error::Error for MRubyCompiler2Error {}
47+
2048
pub struct MRubyCompiler2Context {
2149
c: *mut mrc_ccontext,
2250
}
2351

2452
impl MRubyCompiler2Context {
53+
/// Creates a new MRubyCompiler2Context
2554
pub unsafe fn new() -> Self {
2655
unsafe {
2756
let ccontext = mrc_ccontext_new(null_mut());
2857
MRubyCompiler2Context { c: ccontext }
2958
}
3059
}
3160

32-
pub unsafe fn compile(&mut self, code: &str) -> Vec<u8> {
61+
/// Compiles the given mruby code into mruby bytecode binary
62+
/// Returns the bytecode as a Vec<u8>
63+
pub unsafe fn compile(&mut self, code: &str) -> Result<Vec<u8>, MRubyCompiler2Error> {
3364
unsafe {
34-
let c_code = std::ffi::CString::new(code).unwrap();
65+
let c_code = std::ffi::CString::new(code)
66+
.map_err(|_| MRubyCompiler2Error::new("Code includes null bytes"))?;
3567
let mut ptr = c_code.as_ptr() as *const u8;
36-
let irep = mrc_load_string_cxt(
37-
self.c,
38-
&mut ptr as *mut *const u8,
39-
c_code.as_bytes().len()
40-
);
68+
let irep =
69+
mrc_load_string_cxt(self.c, &mut ptr as *mut *const u8, c_code.as_bytes().len());
4170

4271
if irep.is_null() {
43-
panic!("Failed to compile code");
72+
return Err(MRubyCompiler2Error::new("Failed to compile code"));
4473
}
4574

4675
// Set dummy capacity, deduced from code length
47-
let mut bin: Vec<u8> = Vec::with_capacity(code.len() * 2);
76+
// And leak for safety rather than memory efficiency
77+
let bin: &'static mut [u8] = Vec::with_capacity(code.len() * 2).leak();
4878
let bin_ptr = bin.as_mut_ptr();
4979
let mut bin_size: usize = 0;
5080

@@ -55,14 +85,84 @@ impl MRubyCompiler2Context {
5585
&bin_ptr as *const *mut u8 as *mut *mut u8,
5686
&mut bin_size as *mut usize,
5787
);
88+
mrc_irep_free(self.c, irep as *mut mrc_irep);
5889
if result as u32 != MRC_DUMP_OK {
59-
panic!("Failed to dump irep");
90+
return Err(MRubyCompiler2Error::new("Failed to dump irep binary"));
6091
}
61-
mrc_irep_free(self.c, irep as *mut mrc_irep);
6292

63-
dbg!(bin_size);
6493
let newvec = Vec::from_raw_parts(bin_ptr, bin_size, bin_size);
65-
newvec
94+
Ok(newvec)
95+
}
96+
}
97+
98+
/// Dumps the compiled bytecode of the given mruby code to stdout
99+
#[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+
/// Compiles the given mruby code and writes the bytecode to the specified file path
119+
#[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+
/// Compiles the given mruby code and writes the bytecode as a C function to the specified file path
132+
#[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(())
66166
}
67167
}
68168
}
@@ -73,4 +173,4 @@ impl Drop for MRubyCompiler2Context {
73173
mrc_ccontext_free(self.c);
74174
}
75175
}
76-
}
176+
}

0 commit comments

Comments
 (0)