1- use cxx:: { CxxString , CxxVector } ;
1+ #![ doc = include_str ! ( "../tutorial.md" ) ]
2+ #![ warn( missing_docs) ]
23
4+ /// The main API module for interfacing with CProver tools (`cbmc`, `goto-analyzer`, etc).
35#[ cxx:: bridge]
4- pub mod ffi {
6+ pub mod cprover_api {
57
68 unsafe extern "C++" {
79 include ! ( "libcprover-cpp/api.h" ) ;
810 include ! ( "include/c_api.h" ) ;
911
12+ /// Central organisational handle of the API. This directly corresponds to the
13+ /// C++-API type `api_sessiont`. To initiate a session interaction, call [new_api_session].
1014 type api_sessiont ;
1115
12- // API Functions
16+ /// Provide a unique pointer to the API handle. This will be required to interact
17+ /// with the API calls, and thus, is expected to be the first call before any other
18+ /// interaction with the API.
1319 fn new_api_session ( ) -> UniquePtr < api_sessiont > ;
20+
21+ /// Return the API version - note that this is coming from the C++ API, which
22+ /// returns the API version of CBMC (which should map to the version of `libcprover.a`)
23+ /// the Rust API has mapped against.
1424 fn get_api_version ( & self ) -> UniquePtr < CxxString > ;
25+ /// Provided a C++ Vector of Strings (use [translate_vector_of_string] to translate
26+ /// a Rust `Vec<String` into a `CxxVector<CxxString>` before passing it to the function),
27+ /// load the models from the files in the vector and link them together.
1528 fn load_model_from_files ( & self , files : & CxxVector < CxxString > ) -> Result < ( ) > ;
29+ /// Execute a verification engine run against the loaded model.
30+ /// *ATTENTION*: A model must be loaded before this function is run.
1631 fn verify_model ( & self ) -> Result < ( ) > ;
32+ /// Run a validation check on the goto-model that has been loaded.
33+ /// Corresponds to the CProver CLI option `--validate-goto-model`.
1734 fn validate_goto_model ( & self ) -> Result < ( ) > ;
35+ /// Drop functions that aren't used from the model. Corresponds to
36+ /// the CProver CLI option `--drop-unused-functions`
1837 fn drop_unused_functions ( & self ) -> Result < ( ) > ;
1938
20- // Helper/Utility functions
21- fn translate_vector_of_string ( elements : Vec < String > ) -> & ' static CxxVector < CxxString > ;
39+ // WARNING: Please don't use this function - use its public interface in [ffi_util::translate_rust_vector_to_cpp].
40+ // The reason this is here is that it's implemented on the C++ shim, and to link this function against
41+ // its implementation it needs to be declared within the `unsafe extern "C++"` block of the FFI bridge.
42+ #[ doc( hidden) ]
43+ fn _translate_vector_of_string ( elements : Vec < String > ) -> & ' static CxxVector < CxxString > ;
44+ /// Print messages accumulated into the message buffer from CProver's end.
2245 fn get_messages ( ) -> & ' static CxxVector < CxxString > ;
2346 }
24-
25- extern "Rust" {
26- fn print_response ( vec : & CxxVector < CxxString > ) ;
27- fn translate_response_buffer ( vec : & CxxVector < CxxString > ) -> Vec < String > ;
28- }
2947}
3048
31- /// This is a utility function, whose job is to translate the responses from the C++
32- /// API ( which we get in the form of a C ++ std:: vector<std: string>) into a form that
33- /// can be easily consumed by other Rust programs.
34- pub fn translate_response_buffer ( vec : & CxxVector < CxxString > ) -> Vec < String > {
35- vec. iter ( )
36- . map ( |s| s. to_string_lossy ( ) . into_owned ( ) )
37- . collect ( )
38- }
49+ /// Module containing utility functions for translating between types across
50+ /// the FFI boundary.
51+ pub mod ffi_util {
52+ use crate :: cprover_api:: _translate_vector_of_string;
53+ use cxx:: CxxString ;
54+ use cxx:: CxxVector ;
55+
56+ /// This function translates the responses from the C++ API (which we get in the
57+ /// form of a C++ std::vector<std:string>) into the equivalent Rust type `Vec<String>`.
58+ /// Dual to [translate_rust_vector_to_cpp].
59+ pub fn translate_cpp_vector_to_rust ( vec : & CxxVector < CxxString > ) -> Vec < String > {
60+ vec. iter ( )
61+ . map ( |s| s. to_string_lossy ( ) . into_owned ( ) )
62+ . collect ( )
63+ }
3964
40- /// This is a utility function, whose aim is to simplify direct printing of the messages
41- /// that we get from CBMC's C++ API. Underneath, it's using translate_response_buffer
42- /// to translate the C++ types into Rust types and then prints out the strings contained
43- /// in the resultant rust vector.
44- pub fn print_response ( vec : & CxxVector < CxxString > ) {
45- let vec: Vec < String > = translate_response_buffer ( vec) ;
65+ /// This function aims to simplify direct printing of the messages that we get
66+ /// from CBMC's C++ API.
67+ pub fn print_response ( vec : & Vec < String > ) {
68+ for s in vec {
69+ println ! ( "{}" , s) ;
70+ }
71+ }
4672
47- for s in vec {
48- println ! ( "{}" , s) ;
73+ /// Translate a Rust `Vec<String>` into a C++ acceptable `std::vector<std::string>`.
74+ /// Dual to [translate_cpp_vector_to_rust].
75+ pub fn translate_rust_vector_to_cpp ( elements : Vec < String > ) -> & ' static CxxVector < CxxString > {
76+ _translate_vector_of_string ( elements)
4977 }
5078}
5179
52- // To test run "CBMC_LIB_DIR=<path_to_build/libs> SAT_IMPL=minisat2 cargo test -- --test-threads=1 --nocapture"
80+ // To test run "CBMC_LIB_DIR=<path_to_build/libs> CBMC_VERSION=<version> cargo test -- --test-threads=1 --nocapture"
5381#[ cfg( test) ]
5482mod tests {
5583 use super :: * ;
@@ -58,7 +86,7 @@ mod tests {
5886
5987 #[ test]
6088 fn it_works ( ) {
61- let client = ffi :: new_api_session ( ) ;
89+ let client = cprover_api :: new_api_session ( ) ;
6290 let result = client. get_api_version ( ) ;
6391
6492 let_cxx_string ! ( expected_version = "0.1" ) ;
@@ -69,21 +97,21 @@ mod tests {
6997 fn translate_vector_of_rust_string_to_cpp ( ) {
7098 let vec: Vec < String > = vec ! [ "other/example.c" . to_owned( ) , "/tmp/example2.c" . to_owned( ) ] ;
7199
72- let vect = ffi :: translate_vector_of_string ( vec) ;
100+ let vect = ffi_util :: translate_rust_vector_to_cpp ( vec) ;
73101 assert_eq ! ( vect. len( ) , 2 ) ;
74102 }
75103
76104 #[ test]
77105 fn it_can_load_model_from_file ( ) {
78- let binding = ffi :: new_api_session ( ) ;
106+ let binding = cprover_api :: new_api_session ( ) ;
79107 let client = match binding. as_ref ( ) {
80108 Some ( api_ref) => api_ref,
81109 None => panic ! ( "Failed to acquire API session handle" ) ,
82110 } ;
83111
84112 let vec: Vec < String > = vec ! [ "other/example.c" . to_owned( ) ] ;
85113
86- let vect = ffi :: translate_vector_of_string ( vec) ;
114+ let vect = ffi_util :: translate_rust_vector_to_cpp ( vec) ;
87115 assert_eq ! ( vect. len( ) , 1 ) ;
88116
89117 // Invoke load_model_from_files and see if the model
@@ -104,21 +132,21 @@ mod tests {
104132 // This is also why a print instruction is commented out (as a guide for someone
105133 // else in case they want to inspect the output).
106134 let validation_msg = "Validating consistency of goto-model supplied to API session" ;
107- let msgs = ffi :: get_messages ( ) ;
108- let msgs_assert = translate_response_buffer ( msgs) . clone ( ) ;
135+ let msgs = cprover_api :: get_messages ( ) ;
136+ let msgs_assert = ffi_util :: translate_cpp_vector_to_rust ( msgs) . clone ( ) ;
109137
110138 assert ! ( msgs_assert. contains( & String :: from( validation_msg) ) ) ;
111139
112- // print_response(msgs );
140+ // ffi_util:: print_response(msgs_assert );
113141 }
114142
115143 #[ test]
116144 fn it_can_verify_the_loaded_model ( ) {
117- let client = ffi :: new_api_session ( ) ;
145+ let client = cprover_api :: new_api_session ( ) ;
118146
119147 let vec: Vec < String > = vec ! [ "other/example.c" . to_owned( ) ] ;
120148
121- let vect = ffi :: translate_vector_of_string ( vec) ;
149+ let vect = ffi_util :: translate_rust_vector_to_cpp ( vec) ;
122150
123151 if let Err ( _) = client. load_model_from_files ( vect) {
124152 eprintln ! ( "Failed to load model from files: {:?}" , vect) ;
@@ -138,23 +166,23 @@ mod tests {
138166
139167 let verification_msg = "VERIFICATION FAILED" ;
140168
141- let msgs = ffi :: get_messages ( ) ;
142- let msgs_assert = translate_response_buffer ( msgs) . clone ( ) ;
169+ let msgs = cprover_api :: get_messages ( ) ;
170+ let msgs_assert = ffi_util :: translate_cpp_vector_to_rust ( msgs) . clone ( ) ;
143171
144172 assert ! ( msgs_assert. contains( & String :: from( verification_msg) ) ) ;
145173 }
146174
147175 #[ test]
148176 fn it_can_drop_unused_functions_from_model ( ) {
149- let binding = ffi :: new_api_session ( ) ;
177+ let binding = cprover_api :: new_api_session ( ) ;
150178 let client = match binding. as_ref ( ) {
151179 Some ( api_ref) => api_ref,
152180 None => panic ! ( "Failed to acquire API session handle" ) ,
153181 } ;
154182
155183 let vec: Vec < String > = vec ! [ "other/example.c" . to_owned( ) ] ;
156184
157- let vect = ffi :: translate_vector_of_string ( vec) ;
185+ let vect = ffi_util :: translate_rust_vector_to_cpp ( vec) ;
158186 assert_eq ! ( vect. len( ) , 1 ) ;
159187
160188 if let Err ( _) = client. load_model_from_files ( vect) {
@@ -171,8 +199,8 @@ mod tests {
171199 let instrumentation_msg = "Performing instrumentation pass: dropping unused functions" ;
172200 let instrumentation_msg2 = "Dropping 8 of 11 functions (3 used)" ;
173201
174- let msgs = ffi :: get_messages ( ) ;
175- let msgs_assert = translate_response_buffer ( msgs) . clone ( ) ;
202+ let msgs = cprover_api :: get_messages ( ) ;
203+ let msgs_assert = ffi_util :: translate_cpp_vector_to_rust ( msgs) . clone ( ) ;
176204
177205 assert ! ( msgs_assert. contains( & String :: from( instrumentation_msg) ) ) ;
178206 assert ! ( msgs_assert. contains( & String :: from( instrumentation_msg2) ) ) ;
0 commit comments