11use core:: { ffi, mem, ptr} ;
2+ use std:: mem:: MaybeUninit ;
23use std:: pin:: Pin ;
4+ use std:: sync:: Arc ;
35
46use binaryninjacore_sys:: * ;
57
@@ -18,6 +20,9 @@ pub struct ScriptingProvider {
1820 handle : ptr:: NonNull < BNScriptingProvider > ,
1921}
2022
23+ unsafe impl Sync for ScriptingProvider { }
24+ unsafe impl Send for ScriptingProvider { }
25+
2126impl ScriptingProvider {
2227 pub ( crate ) unsafe fn from_raw ( handle : ptr:: NonNull < BNScriptingProvider > ) -> Self {
2328 Self { handle }
@@ -95,30 +100,9 @@ impl ScriptingProvider {
95100 }
96101 }
97102
98- pub fn new_instance < S : ScriptingInstanceCallbacks > ( & self ) -> ScriptingInstance {
99- // SAFETY freed by cb_destroy_instance
100- let uninit = Box :: leak ( Box :: new ( mem:: MaybeUninit :: zeroed ( ) ) ) ;
101- let mut callbacks = BNScriptingInstanceCallbacks {
102- context : unsafe { uninit. assume_init_mut ( ) as * mut S as * mut ffi:: c_void } ,
103- destroyInstance : Some ( cb_destroy_instance :: < S > ) ,
104- externalRefTaken : Some ( cb_external_ref_taken :: < S > ) ,
105- externalRefReleased : Some ( cb_external_ref_released :: < S > ) ,
106- executeScriptInput : Some ( cb_execute_script_input :: < S > ) ,
107- executeScriptInputFromFilename : Some ( cb_execute_script_input_from_filename :: < S > ) ,
108- cancelScriptInput : Some ( cb_cancel_script_input :: < S > ) ,
109- releaseBinaryView : Some ( cb_release_binary_view :: < S > ) ,
110- setCurrentBinaryView : Some ( cb_set_current_binary_view :: < S > ) ,
111- setCurrentFunction : Some ( cb_set_current_function :: < S > ) ,
112- setCurrentBasicBlock : Some ( cb_set_current_basic_block :: < S > ) ,
113- setCurrentAddress : Some ( cb_set_current_address :: < S > ) ,
114- setCurrentSelection : Some ( cb_set_current_selection :: < S > ) ,
115- completeInput : Some ( cb_complete_input :: < S > ) ,
116- stop : Some ( cb_stop :: < S > ) ,
117- } ;
118- let result = unsafe { BNInitScriptingInstance ( self . as_raw ( ) , & mut callbacks) } ;
119- let instance = unsafe { ScriptingInstance :: from_raw ( ptr:: NonNull :: new ( result) . unwrap ( ) ) } ;
120- uninit. write ( S :: new ( * self , & instance) ) ;
121- instance
103+ pub fn new_instance ( & self ) -> Ref < ScriptingInstance > {
104+ let instance = unsafe { BNCreateScriptingProviderInstance ( self . as_raw ( ) ) } ;
105+ unsafe { ScriptingInstance :: ref_from_raw ( ptr:: NonNull :: new ( instance) . unwrap ( ) ) }
122106 }
123107}
124108
@@ -143,6 +127,9 @@ pub struct ScriptingInstance {
143127 handle : ptr:: NonNull < BNScriptingInstance > ,
144128}
145129
130+ unsafe impl Sync for ScriptingInstance { }
131+ unsafe impl Send for ScriptingInstance { }
132+
146133impl ToOwned for ScriptingInstance {
147134 type Owned = Ref < Self > ;
148135
@@ -163,18 +150,10 @@ unsafe impl RefCountable for ScriptingInstance {
163150}
164151
165152impl ScriptingInstance {
166- pub ( crate ) unsafe fn from_raw ( handle : ptr:: NonNull < BNScriptingInstance > ) -> Self {
167- Self { handle }
168- }
169-
170153 pub ( crate ) unsafe fn ref_from_raw ( handle : ptr:: NonNull < BNScriptingInstance > ) -> Ref < Self > {
171154 Ref :: new ( Self { handle } )
172155 }
173156
174- pub ( crate ) unsafe fn into_raw ( self ) -> * mut BNScriptingInstance {
175- mem:: ManuallyDrop :: new ( self ) . handle . as_ptr ( )
176- }
177-
178157 #[ allow( clippy:: mut_from_ref) ]
179158 pub ( crate ) unsafe fn as_raw ( & self ) -> & mut BNScriptingInstance {
180159 & mut * self . handle . as_ptr ( )
@@ -214,10 +193,10 @@ impl ScriptingInstance {
214193 unsafe { BNNotifyInputReadyStateForScriptingInstance ( self . as_raw ( ) , state) }
215194 }
216195
217- pub fn register_output_listener < L : ScriptingOutputListener > (
218- self ,
196+ pub fn register_output_listener < ' a , L : ScriptingOutputListener > (
197+ & ' a self ,
219198 listener : L ,
220- ) -> ScriptingInstanceWithListener < L > {
199+ ) -> ScriptingInstanceWithListener < ' a , L > {
221200 let mut listener = Box :: pin ( listener) ;
222201 let mut callbacks = BNScriptingOutputListener {
223202 context : unsafe { listener. as_mut ( ) . get_unchecked_mut ( ) } as * mut _ as * mut ffi:: c_void ,
@@ -323,26 +302,26 @@ impl ScriptingInstance {
323302 }
324303}
325304
326- pub struct ScriptingInstanceWithListener < L : ScriptingOutputListener > {
327- handle : ScriptingInstance ,
305+ pub struct ScriptingInstanceWithListener < ' a , L : ScriptingOutputListener > {
306+ handle : & ' a ScriptingInstance ,
328307 listener : Pin < Box < L > > ,
329308}
330309
331- impl < L : ScriptingOutputListener > AsRef < ScriptingInstance > for ScriptingInstanceWithListener < L > {
332- fn as_ref ( & self ) -> & ScriptingInstance {
333- & self . handle
310+ impl < L : ScriptingOutputListener > AsRef < L > for ScriptingInstanceWithListener < ' _ , L > {
311+ fn as_ref ( & self ) -> & L {
312+ & self . listener
334313 }
335314}
336315
337- impl < L : ScriptingOutputListener > core:: ops:: Deref for ScriptingInstanceWithListener < L > {
338- type Target = ScriptingInstance ;
316+ impl < L : ScriptingOutputListener > core:: ops:: Deref for ScriptingInstanceWithListener < ' _ , L > {
317+ type Target = L ;
339318 fn deref ( & self ) -> & Self :: Target {
340- & self . handle
319+ & self . listener
341320 }
342321}
343322
344- impl < L : ScriptingOutputListener > ScriptingInstanceWithListener < L > {
345- pub fn unregister ( mut self ) -> ScriptingInstance {
323+ impl < L : ScriptingOutputListener > ScriptingInstanceWithListener < ' _ , L > {
324+ pub fn unregister ( mut self ) {
346325 let mut callbacks = BNScriptingOutputListener {
347326 context : unsafe { self . listener . as_mut ( ) . get_unchecked_mut ( ) } as * mut _
348327 as * mut ffi:: c_void ,
@@ -354,28 +333,29 @@ impl<L: ScriptingOutputListener> ScriptingInstanceWithListener<L> {
354333 unsafe {
355334 BNUnregisterScriptingInstanceOutputListener ( self . handle . as_raw ( ) , & mut callbacks)
356335 } ;
357- // drop the listener
358- let Self {
359- handle : instance,
360- listener : _,
361- } = self ;
362- // return the inner instance
363- instance
364336 }
365337}
366338
367339pub trait ScriptingCustomProvider : Sync + Send {
340+ type Instance : ScriptingInstanceCallbacks ;
368341 fn new ( core : ScriptingProvider ) -> Self ;
369- fn create_instance ( & self ) -> ScriptingInstance ;
342+ fn init_instance ( & self , handle : CoreScriptingInstance ) -> Self :: Instance ;
343+ fn get_core ( & self ) -> & ScriptingProvider ;
370344 fn load_module ( & self , repo_path : & str , plugin_path : & str , force : bool ) -> bool ;
371345 fn install_modules ( & self , modules : & str ) -> bool ;
346+
347+ fn new_instance ( & self ) -> ( Arc < Self :: Instance > , Ref < ScriptingInstance > )
348+ where
349+ Self : Sized ,
350+ {
351+ register_instance :: < Self > ( self )
352+ }
372353}
373354
355+ pub struct CoreScriptingInstance ( Ref < ScriptingInstance > ) ;
356+
374357pub trait ScriptingInstanceCallbacks : Sync + Send {
375- fn new ( provider : ScriptingProvider , handle : & ScriptingInstance ) -> Self ;
376- fn destroy_instance ( & self ) ;
377- fn external_ref_taken ( & self ) ;
378- fn external_ref_released ( & self ) ;
358+ fn get_core ( & self ) -> & CoreScriptingInstance ;
379359 fn execute_script_input ( & self , input : & str ) -> ScriptingProviderExecuteResult ;
380360 fn execute_script_input_from_filename ( & self , input : & str ) -> ScriptingProviderExecuteResult ;
381361 fn cancel_script_input ( & self ) ;
@@ -387,6 +367,22 @@ pub trait ScriptingInstanceCallbacks: Sync + Send {
387367 fn set_current_selection ( & self , begin : u64 , end : u64 ) ;
388368 fn complete_input ( & self , text : & str , state : u64 ) -> String ;
389369 fn stop ( & self ) ;
370+
371+ fn notify_output ( & self , text : & str ) {
372+ self . get_core ( ) . 0 . notify_output ( text) ;
373+ }
374+ fn notify_warning ( & self , text : & str ) {
375+ self . get_core ( ) . 0 . notify_warning ( text) ;
376+ }
377+ fn notify_error ( & self , text : & str ) {
378+ self . get_core ( ) . 0 . notify_error ( text) ;
379+ }
380+ fn register_output_listener < ' a , L : ScriptingOutputListener > (
381+ & ' a self ,
382+ listener : L ,
383+ ) -> ScriptingInstanceWithListener < ' a , L > {
384+ self . get_core ( ) . 0 . register_output_listener ( listener)
385+ }
390386}
391387
392388pub trait ScriptingOutputListener : Sync + Send {
@@ -404,7 +400,6 @@ where
404400{
405401 let name = name. into_bytes_with_nul ( ) ;
406402 let api_name = api_name. into_bytes_with_nul ( ) ;
407- // SAFETY: Websocket provider is never freed
408403 let provider_uinit = Box :: leak ( Box :: new ( mem:: MaybeUninit :: zeroed ( ) ) ) ;
409404 let result = unsafe {
410405 BNRegisterScriptingProvider (
@@ -423,11 +418,59 @@ where
423418 ( unsafe { provider_uinit. assume_init_ref ( ) } , provider_core)
424419}
425420
421+ fn register_instance < S : ScriptingCustomProvider > (
422+ provider : & S ,
423+ ) -> ( Arc < S :: Instance > , Ref < ScriptingInstance > ) {
424+ let instance_uninit: * const MaybeUninit < S :: Instance > = Arc :: into_raw ( Arc :: new_uninit ( ) ) ;
425+ let mut callbacks = BNScriptingInstanceCallbacks {
426+ context : instance_uninit as * mut ffi:: c_void ,
427+ destroyInstance : Some ( cb_destroy_instance :: < S :: Instance > ) ,
428+ externalRefTaken : Some ( cb_external_ref_taken :: < S :: Instance > ) ,
429+ externalRefReleased : Some ( cb_external_ref_released :: < S :: Instance > ) ,
430+ executeScriptInput : Some ( cb_execute_script_input :: < S :: Instance > ) ,
431+ executeScriptInputFromFilename : Some ( cb_execute_script_input_from_filename :: < S :: Instance > ) ,
432+ cancelScriptInput : Some ( cb_cancel_script_input :: < S :: Instance > ) ,
433+ releaseBinaryView : Some ( cb_release_binary_view :: < S :: Instance > ) ,
434+ setCurrentBinaryView : Some ( cb_set_current_binary_view :: < S :: Instance > ) ,
435+ setCurrentFunction : Some ( cb_set_current_function :: < S :: Instance > ) ,
436+ setCurrentBasicBlock : Some ( cb_set_current_basic_block :: < S :: Instance > ) ,
437+ setCurrentAddress : Some ( cb_set_current_address :: < S :: Instance > ) ,
438+ setCurrentSelection : Some ( cb_set_current_selection :: < S :: Instance > ) ,
439+ completeInput : Some ( cb_complete_input :: < S :: Instance > ) ,
440+ stop : Some ( cb_stop :: < S :: Instance > ) ,
441+ } ;
442+ let handle = unsafe { BNInitScriptingInstance ( provider. get_core ( ) . as_raw ( ) , & mut callbacks) } ;
443+ let core_instance = unsafe {
444+ CoreScriptingInstance ( ScriptingInstance :: ref_from_raw (
445+ ptr:: NonNull :: new ( handle) . unwrap ( ) ,
446+ ) )
447+ } ;
448+ let new_instance = provider. init_instance ( core_instance) ;
449+
450+ // recreate the Arc, initialize it, clone it, then leak it again
451+ let instance: Arc < _ > = unsafe {
452+ let mut instance_uninit = Arc :: from_raw ( instance_uninit) ;
453+ Arc :: get_mut ( & mut instance_uninit)
454+ . unwrap ( )
455+ . write ( new_instance) ;
456+ let instance = Arc :: < MaybeUninit < _ > > :: assume_init ( instance_uninit) ;
457+ let result = Arc :: clone ( & instance) ;
458+ // leak, this will be owned by the [ScriptingInstance]
459+ let _ptr = Arc :: into_raw ( instance) ;
460+ result
461+ } ;
462+
463+ let core_instance =
464+ unsafe { ScriptingInstance :: ref_from_raw ( ptr:: NonNull :: new ( handle) . unwrap ( ) ) } ;
465+ ( instance, core_instance)
466+ }
467+
426468unsafe extern "C" fn cb_create_instance < S : ScriptingCustomProvider > (
427469 ctxt : * mut ffi:: c_void ,
428470) -> * mut BNScriptingInstance {
429471 let ctxt = & mut * ( ctxt as * mut S ) ;
430- ctxt. create_instance ( ) . into_raw ( )
472+ let ( _rust_instance, core_instance) = register_instance ( ctxt) ;
473+ core_instance. as_raw ( )
431474}
432475
433476unsafe extern "C" fn cb_load_module < S : ScriptingCustomProvider > (
@@ -456,19 +499,17 @@ unsafe extern "C" fn cb_install_modules<S: ScriptingCustomProvider>(
456499}
457500
458501unsafe extern "C" fn cb_destroy_instance < S : ScriptingInstanceCallbacks > ( ctxt : * mut ffi:: c_void ) {
459- drop ( Box :: from_raw ( ctxt as * mut S ) )
502+ Arc :: from_raw ( ctxt as * mut S ) ;
460503}
461504
462505unsafe extern "C" fn cb_external_ref_taken < S : ScriptingInstanceCallbacks > ( ctxt : * mut ffi:: c_void ) {
463- let ctxt = & mut * ( ctxt as * mut S ) ;
464- ctxt. external_ref_taken ( )
506+ Arc :: increment_strong_count ( ctxt as * mut S ) ;
465507}
466508
467509unsafe extern "C" fn cb_external_ref_released < S : ScriptingInstanceCallbacks > (
468510 ctxt : * mut ffi:: c_void ,
469511) {
470- let ctxt = & mut * ( ctxt as * mut S ) ;
471- ctxt. external_ref_released ( )
512+ Arc :: decrement_strong_count ( ctxt as * mut S ) ;
472513}
473514
474515unsafe extern "C" fn cb_execute_script_input < S : ScriptingInstanceCallbacks > (
@@ -477,7 +518,8 @@ unsafe extern "C" fn cb_execute_script_input<S: ScriptingInstanceCallbacks>(
477518) -> BNScriptingProviderExecuteResult {
478519 let input = ffi:: CStr :: from_ptr ( input) ;
479520 let ctxt = & mut * ( ctxt as * mut S ) ;
480- ctxt. execute_script_input ( & input. to_string_lossy ( ) )
521+ let result = ctxt. execute_script_input ( & input. to_string_lossy ( ) ) ;
522+ result
481523}
482524
483525unsafe extern "C" fn cb_execute_script_input_from_filename < S : ScriptingInstanceCallbacks > (
0 commit comments