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,27 @@ 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
+ isolate. pending_dyn_imports . push ( fut) ;
229
+ } else {
230
+ panic ! ( "dyn_import callback not set" )
231
+ }
232
+ }
233
+
177
234
extern "C" fn pre_dispatch (
178
235
user_data : * mut c_void ,
179
236
control_argv0 : deno_buf ,
@@ -340,6 +397,26 @@ impl Isolate {
340
397
assert_ne ! ( snapshot. data_len, 0 ) ;
341
398
Ok ( snapshot)
342
399
}
400
+
401
+ fn dyn_import_done (
402
+ & self ,
403
+ id : libdeno:: deno_dyn_import_id ,
404
+ mod_id : deno_mod ,
405
+ ) -> Result < ( ) , JSError > {
406
+ unsafe {
407
+ libdeno:: deno_dyn_import (
408
+ self . libdeno_isolate ,
409
+ self . as_raw_ptr ( ) ,
410
+ id,
411
+ mod_id,
412
+ )
413
+ } ;
414
+ if let Some ( js_error) = self . last_exception ( ) {
415
+ assert_eq ! ( id, 0 ) ;
416
+ return Err ( js_error) ;
417
+ }
418
+ Ok ( ( ) )
419
+ }
343
420
}
344
421
345
422
/// Called during mod_instantiate() to resolve imports.
@@ -440,6 +517,21 @@ impl Future for Isolate {
440
517
let mut overflow_response: Option < Buf > = None ;
441
518
442
519
loop {
520
+ // If there are any pending dyn_import futures, do those first.
521
+ if !self . pending_dyn_imports . is_empty ( ) {
522
+ match self . pending_dyn_imports . poll ( ) {
523
+ Err ( ( ) ) => panic ! ( "unexpected dyn_import error" ) ,
524
+ Ok ( Ready ( None ) ) => break ,
525
+ Ok ( NotReady ) => break ,
526
+ Ok ( Ready ( Some ( ( dyn_import_id, mod_id) ) ) ) => {
527
+ debug ! ( "dyn_import_done {} {}" , dyn_import_id, mod_id) ;
528
+ self . dyn_import_done ( dyn_import_id, mod_id) ?;
529
+ }
530
+ }
531
+ }
532
+
533
+ // Now handle actual ops,
534
+
443
535
self . have_unpolled_ops = false ;
444
536
#[ allow( clippy:: match_wild_err_arm) ]
445
537
match self . pending_ops . poll ( ) {
@@ -764,6 +856,95 @@ pub mod tests {
764
856
} ) ;
765
857
}
766
858
859
+ #[ test]
860
+ fn dyn_import_err ( ) {
861
+ // Test an erroneous dynamic import where the specified module isn't found.
862
+ run_in_task ( || {
863
+ let count = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
864
+ let count_ = count. clone ( ) ;
865
+ let mut config = Config :: default ( ) ;
866
+ config. dyn_import ( move |specifier, referrer| {
867
+ count_. fetch_add ( 1 , Ordering :: Relaxed ) ;
868
+ assert_eq ! ( specifier, "foo.js" ) ;
869
+ assert_eq ! ( referrer, "dyn_import2.js" ) ;
870
+ Box :: new ( futures:: future:: err ( ( ) ) )
871
+ } ) ;
872
+ let mut isolate = Isolate :: new ( StartupData :: None , config) ;
873
+ js_check ( isolate. execute (
874
+ "dyn_import2.js" ,
875
+ r#"
876
+ (async () => {
877
+ await import("foo.js");
878
+ })();
879
+ "# ,
880
+ ) ) ;
881
+ assert_eq ! ( count. load( Ordering :: Relaxed ) , 1 ) ;
882
+
883
+ // We should get an error here.
884
+ let result = isolate. poll ( ) ;
885
+ assert ! ( result. is_err( ) ) ;
886
+ } )
887
+ }
888
+
889
+ #[ test]
890
+ fn dyn_import_ok ( ) {
891
+ run_in_task ( || {
892
+ let count = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
893
+ let count_ = count. clone ( ) ;
894
+
895
+ // Sometimes Rust is really annoying.
896
+ use std:: sync:: Mutex ;
897
+ let mod_b = Arc :: new ( Mutex :: new ( 0 ) ) ;
898
+ let mod_b2 = mod_b. clone ( ) ;
899
+
900
+ let mut config = Config :: default ( ) ;
901
+ config. dyn_import ( move |_specifier, referrer| {
902
+ count_. fetch_add ( 1 , Ordering :: Relaxed ) ;
903
+ // assert_eq!(specifier, "foo.js");
904
+ assert_eq ! ( referrer, "dyn_import3.js" ) ;
905
+ let mod_id = mod_b2. lock ( ) . unwrap ( ) ;
906
+ Box :: new ( futures:: future:: ok ( * mod_id) )
907
+ } ) ;
908
+
909
+ let mut isolate = Isolate :: new ( StartupData :: None , config) ;
910
+
911
+ // Instantiate mod_b
912
+ {
913
+ let mut mod_id = mod_b. lock ( ) . unwrap ( ) ;
914
+ * mod_id = isolate
915
+ . mod_new ( false , "b.js" , "export function b() { return 'b' }" )
916
+ . unwrap ( ) ;
917
+ let mut resolve = move |_specifier : & str ,
918
+ _referrer : deno_mod |
919
+ -> deno_mod { unreachable ! ( ) } ;
920
+ js_check ( isolate. mod_instantiate ( * mod_id, & mut resolve) ) ;
921
+ }
922
+ // Dynamically import mod_b
923
+ js_check ( isolate. execute (
924
+ "dyn_import3.js" ,
925
+ r#"
926
+ (async () => {
927
+ let mod = await import("foo1.js");
928
+ if (mod.b() !== 'b') {
929
+ throw Error("bad1");
930
+ }
931
+ // And again!
932
+ mod = await import("foo2.js");
933
+ if (mod.b() !== 'b') {
934
+ throw Error("bad2");
935
+ }
936
+ })();
937
+ "# ,
938
+ ) ) ;
939
+
940
+ assert_eq ! ( count. load( Ordering :: Relaxed ) , 1 ) ;
941
+ assert_eq ! ( Ok ( Ready ( ( ) ) ) , isolate. poll( ) ) ;
942
+ assert_eq ! ( count. load( Ordering :: Relaxed ) , 2 ) ;
943
+ assert_eq ! ( Ok ( Ready ( ( ) ) ) , isolate. poll( ) ) ;
944
+ assert_eq ! ( count. load( Ordering :: Relaxed ) , 2 ) ;
945
+ } )
946
+ }
947
+
767
948
#[ test]
768
949
fn terminate_execution ( ) {
769
950
let ( tx, rx) = std:: sync:: mpsc:: channel :: < bool > ( ) ;
0 commit comments