7
7
use crate :: js_errors:: JSError ;
8
8
use crate :: libdeno;
9
9
use crate :: libdeno:: deno_buf;
10
+ use crate :: libdeno:: deno_dyn_import_id;
10
11
use crate :: libdeno:: deno_mod;
11
12
use crate :: libdeno:: deno_pinned_buf;
12
13
use crate :: libdeno:: PinnedBuf ;
@@ -19,6 +20,7 @@ use futures::task;
19
20
use futures:: Async :: * ;
20
21
use futures:: Future ;
21
22
use futures:: Poll ;
23
+ use libc:: c_char;
22
24
use libc:: c_void;
23
25
use std:: ffi:: CStr ;
24
26
use std:: ffi:: CString ;
@@ -52,9 +54,33 @@ pub enum StartupData<'a> {
52
54
53
55
type DispatchFn = Fn ( & [ u8 ] , Option < PinnedBuf > ) -> Op ;
54
56
57
+ pub type DynImportFuture = Box < dyn Future < Item = deno_mod , Error = ( ) > + Send > ;
58
+ type DynImportFn = Fn ( & str , & str ) -> DynImportFuture ;
59
+
60
+ /// Wraps DynImportFuture to include the deno_dyn_import_id, so that it doesn't
61
+ /// need to be exposed.
62
+ struct DynImport {
63
+ id : deno_dyn_import_id ,
64
+ inner : DynImportFuture ,
65
+ }
66
+
67
+ impl Future for DynImport {
68
+ type Item = ( deno_dyn_import_id , deno_mod ) ;
69
+ type Error = ( ) ;
70
+ fn poll ( & mut self ) -> Poll < Self :: Item , ( ) > {
71
+ match self . inner . poll ( ) {
72
+ Ok ( Ready ( mod_id) ) => Ok ( Ready ( ( self . id , mod_id) ) ) ,
73
+ Ok ( NotReady ) => Ok ( NotReady ) ,
74
+ // Note that mod_id 0 indicates error.
75
+ Err ( ( ) ) => Ok ( Ready ( ( self . id , 0 ) ) ) ,
76
+ }
77
+ }
78
+ }
79
+
55
80
#[ derive( Default ) ]
56
81
pub struct Config {
57
82
dispatch : Option < Arc < DispatchFn > > ,
83
+ dyn_import : Option < Arc < DynImportFn > > ,
58
84
pub will_snapshot : bool ,
59
85
}
60
86
@@ -68,6 +94,13 @@ impl Config {
68
94
{
69
95
self . dispatch = Some ( Arc :: new ( f) ) ;
70
96
}
97
+
98
+ pub fn dyn_import < F > ( & mut self , f : F )
99
+ where
100
+ F : Fn ( & str , & str ) -> DynImportFuture + Send + Sync + ' static ,
101
+ {
102
+ self . dyn_import = Some ( Arc :: new ( f) ) ;
103
+ }
71
104
}
72
105
73
106
/// A single execution context of JavaScript. Corresponds roughly to the "Web
@@ -85,6 +118,7 @@ pub struct Isolate {
85
118
needs_init : bool ,
86
119
shared : SharedQueue ,
87
120
pending_ops : FuturesUnordered < OpAsyncFuture > ,
121
+ pending_dyn_imports : FuturesUnordered < DynImport > ,
88
122
have_unpolled_ops : bool ,
89
123
}
90
124
@@ -121,6 +155,7 @@ impl Isolate {
121
155
load_snapshot : Snapshot2 :: empty ( ) ,
122
156
shared : shared. as_deno_buf ( ) ,
123
157
recv_cb : Self :: pre_dispatch,
158
+ dyn_import_cb : Self :: dyn_import,
124
159
} ;
125
160
126
161
// Seperate into Option values for each startup type
@@ -147,6 +182,7 @@ impl Isolate {
147
182
needs_init,
148
183
pending_ops : FuturesUnordered :: new ( ) ,
149
184
have_unpolled_ops : false ,
185
+ pending_dyn_imports : FuturesUnordered :: new ( ) ,
150
186
} ;
151
187
152
188
// If we want to use execute this has to happen here sadly.
@@ -174,6 +210,28 @@ impl Isolate {
174
210
}
175
211
}
176
212
213
+ extern "C" fn dyn_import (
214
+ user_data : * mut c_void ,
215
+ specifier : * const c_char ,
216
+ referrer : * const c_char ,
217
+ id : deno_dyn_import_id ,
218
+ ) {
219
+ assert_ne ! ( user_data, std:: ptr:: null_mut( ) ) ;
220
+ let isolate = unsafe { Isolate :: from_raw_ptr ( user_data) } ;
221
+ let specifier = unsafe { CStr :: from_ptr ( specifier) . to_str ( ) . unwrap ( ) } ;
222
+ let referrer = unsafe { CStr :: from_ptr ( referrer) . to_str ( ) . unwrap ( ) } ;
223
+ debug ! ( "dyn_import specifier {} referrer {} " , specifier, referrer) ;
224
+
225
+ if let Some ( ref f) = isolate. config . dyn_import {
226
+ let inner = f ( specifier, referrer) ;
227
+ let fut = DynImport { inner, id } ;
228
+ task:: current ( ) . notify ( ) ;
229
+ isolate. pending_dyn_imports . push ( fut) ;
230
+ } else {
231
+ panic ! ( "dyn_import callback not set" )
232
+ }
233
+ }
234
+
177
235
extern "C" fn pre_dispatch (
178
236
user_data : * mut c_void ,
179
237
control_argv0 : deno_buf ,
@@ -340,6 +398,27 @@ impl Isolate {
340
398
assert_ne ! ( snapshot. data_len, 0 ) ;
341
399
Ok ( snapshot)
342
400
}
401
+
402
+ fn dyn_import_done (
403
+ & self ,
404
+ id : libdeno:: deno_dyn_import_id ,
405
+ mod_id : deno_mod ,
406
+ ) -> Result < ( ) , JSError > {
407
+ debug ! ( "dyn_import_done {} {}" , id, mod_id) ;
408
+ unsafe {
409
+ libdeno:: deno_dyn_import (
410
+ self . libdeno_isolate ,
411
+ self . as_raw_ptr ( ) ,
412
+ id,
413
+ mod_id,
414
+ )
415
+ } ;
416
+ if let Some ( js_error) = self . last_exception ( ) {
417
+ assert_eq ! ( id, 0 ) ;
418
+ return Err ( js_error) ;
419
+ }
420
+ Ok ( ( ) )
421
+ }
343
422
}
344
423
345
424
/// Called during mod_instantiate() to resolve imports.
@@ -440,6 +519,18 @@ impl Future for Isolate {
440
519
let mut overflow_response: Option < Buf > = None ;
441
520
442
521
loop {
522
+ // If there are any pending dyn_import futures, do those first.
523
+ match self . pending_dyn_imports . poll ( ) {
524
+ Err ( ( ) ) => unreachable ! ( ) ,
525
+ Ok ( NotReady ) => unreachable ! ( ) ,
526
+ Ok ( Ready ( None ) ) => ( ) ,
527
+ Ok ( Ready ( Some ( ( dyn_import_id, mod_id) ) ) ) => {
528
+ self . dyn_import_done ( dyn_import_id, mod_id) ?;
529
+ continue ;
530
+ }
531
+ }
532
+
533
+ // Now handle actual ops.
443
534
self . have_unpolled_ops = false ;
444
535
#[ allow( clippy:: match_wild_err_arm) ]
445
536
match self . pending_ops . poll ( ) {
@@ -764,6 +855,95 @@ pub mod tests {
764
855
} ) ;
765
856
}
766
857
858
+ #[ test]
859
+ fn dyn_import_err ( ) {
860
+ // Test an erroneous dynamic import where the specified module isn't found.
861
+ run_in_task ( || {
862
+ let count = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
863
+ let count_ = count. clone ( ) ;
864
+ let mut config = Config :: default ( ) ;
865
+ config. dyn_import ( move |specifier, referrer| {
866
+ count_. fetch_add ( 1 , Ordering :: Relaxed ) ;
867
+ assert_eq ! ( specifier, "foo.js" ) ;
868
+ assert_eq ! ( referrer, "dyn_import2.js" ) ;
869
+ Box :: new ( futures:: future:: err ( ( ) ) )
870
+ } ) ;
871
+ let mut isolate = Isolate :: new ( StartupData :: None , config) ;
872
+ js_check ( isolate. execute (
873
+ "dyn_import2.js" ,
874
+ r#"
875
+ (async () => {
876
+ await import("foo.js");
877
+ })();
878
+ "# ,
879
+ ) ) ;
880
+ assert_eq ! ( count. load( Ordering :: Relaxed ) , 1 ) ;
881
+
882
+ // We should get an error here.
883
+ let result = isolate. poll ( ) ;
884
+ assert ! ( result. is_err( ) ) ;
885
+ } )
886
+ }
887
+
888
+ #[ test]
889
+ fn dyn_import_ok ( ) {
890
+ run_in_task ( || {
891
+ let count = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
892
+ let count_ = count. clone ( ) ;
893
+
894
+ // Sometimes Rust is really annoying.
895
+ use std:: sync:: Mutex ;
896
+ let mod_b = Arc :: new ( Mutex :: new ( 0 ) ) ;
897
+ let mod_b2 = mod_b. clone ( ) ;
898
+
899
+ let mut config = Config :: default ( ) ;
900
+ config. dyn_import ( move |_specifier, referrer| {
901
+ count_. fetch_add ( 1 , Ordering :: Relaxed ) ;
902
+ // assert_eq!(specifier, "foo.js");
903
+ assert_eq ! ( referrer, "dyn_import3.js" ) ;
904
+ let mod_id = mod_b2. lock ( ) . unwrap ( ) ;
905
+ Box :: new ( futures:: future:: ok ( * mod_id) )
906
+ } ) ;
907
+
908
+ let mut isolate = Isolate :: new ( StartupData :: None , config) ;
909
+
910
+ // Instantiate mod_b
911
+ {
912
+ let mut mod_id = mod_b. lock ( ) . unwrap ( ) ;
913
+ * mod_id = isolate
914
+ . mod_new ( false , "b.js" , "export function b() { return 'b' }" )
915
+ . unwrap ( ) ;
916
+ let mut resolve = move |_specifier : & str ,
917
+ _referrer : deno_mod |
918
+ -> deno_mod { unreachable ! ( ) } ;
919
+ js_check ( isolate. mod_instantiate ( * mod_id, & mut resolve) ) ;
920
+ }
921
+ // Dynamically import mod_b
922
+ js_check ( isolate. execute (
923
+ "dyn_import3.js" ,
924
+ r#"
925
+ (async () => {
926
+ let mod = await import("foo1.js");
927
+ if (mod.b() !== 'b') {
928
+ throw Error("bad1");
929
+ }
930
+ // And again!
931
+ mod = await import("foo2.js");
932
+ if (mod.b() !== 'b') {
933
+ throw Error("bad2");
934
+ }
935
+ })();
936
+ "# ,
937
+ ) ) ;
938
+
939
+ assert_eq ! ( count. load( Ordering :: Relaxed ) , 1 ) ;
940
+ assert_eq ! ( Ok ( Ready ( ( ) ) ) , isolate. poll( ) ) ;
941
+ assert_eq ! ( count. load( Ordering :: Relaxed ) , 2 ) ;
942
+ assert_eq ! ( Ok ( Ready ( ( ) ) ) , isolate. poll( ) ) ;
943
+ assert_eq ! ( count. load( Ordering :: Relaxed ) , 2 ) ;
944
+ } )
945
+ }
946
+
767
947
#[ test]
768
948
fn terminate_execution ( ) {
769
949
let ( tx, rx) = std:: sync:: mpsc:: channel :: < bool > ( ) ;
0 commit comments