@@ -7,44 +7,74 @@ mod bindings {
77 include ! ( "./bindings.rs" ) ;
88}
99use 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+
2048pub struct MRubyCompiler2Context {
2149 c : * mut mrc_ccontext ,
2250}
2351
2452impl 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