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
+ #[ allow( unused_variables) ]
214
+ extern "C" fn dyn_import (
215
+ user_data : * mut c_void ,
216
+ specifier : * const c_char ,
217
+ referrer : * const c_char ,
218
+ id : deno_dyn_import_id ,
219
+ ) {
220
+ assert_ne ! ( user_data, std:: ptr:: null_mut( ) ) ;
221
+ let isolate = unsafe { Isolate :: from_raw_ptr ( user_data) } ;
222
+ let specifier = unsafe { CStr :: from_ptr ( specifier) . to_str ( ) . unwrap ( ) } ;
223
+ let referrer = unsafe { CStr :: from_ptr ( referrer) . to_str ( ) . unwrap ( ) } ;
224
+ debug ! ( "dyn_import specifier {} referrer {} " , specifier, referrer) ;
225
+
226
+ if let Some ( ref f) = isolate. config . dyn_import {
227
+ let inner = f ( specifier, referrer) ;
228
+ let fut = DynImport { inner, id } ;
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,26 @@ 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
+ unsafe {
408
+ libdeno:: deno_dyn_import (
409
+ self . libdeno_isolate ,
410
+ self . as_raw_ptr ( ) ,
411
+ id,
412
+ mod_id,
413
+ )
414
+ } ;
415
+ if let Some ( js_error) = self . last_exception ( ) {
416
+ assert_eq ! ( id, 0 ) ;
417
+ return Err ( js_error) ;
418
+ }
419
+ Ok ( ( ) )
420
+ }
343
421
}
344
422
345
423
/// Called during mod_instantiate() to resolve imports.
@@ -440,6 +518,21 @@ impl Future for Isolate {
440
518
let mut overflow_response: Option < Buf > = None ;
441
519
442
520
loop {
521
+ // If there are any pending dyn_import futures, do those first.
522
+ if !self . pending_dyn_imports . is_empty ( ) {
523
+ match self . pending_dyn_imports . poll ( ) {
524
+ Err ( ( ) ) => panic ! ( "unexpected dyn_import error" ) ,
525
+ Ok ( Ready ( None ) ) => break ,
526
+ Ok ( NotReady ) => break ,
527
+ Ok ( Ready ( Some ( ( dyn_import_id, mod_id) ) ) ) => {
528
+ debug ! ( "dyn_import_done {} {}" , dyn_import_id, mod_id) ;
529
+ self . dyn_import_done ( dyn_import_id, mod_id) ?;
530
+ }
531
+ }
532
+ }
533
+
534
+ // Now handle actual ops,
535
+
443
536
self . have_unpolled_ops = false ;
444
537
#[ allow( clippy:: match_wild_err_arm) ]
445
538
match self . pending_ops . poll ( ) {
@@ -764,6 +857,121 @@ pub mod tests {
764
857
} ) ;
765
858
}
766
859
860
+ #[ test]
861
+ fn test_dyn_import1 ( ) {
862
+ // Just check that we properly get the callback with the right specifier and
863
+ // referrer.
864
+ run_in_task ( || {
865
+ let count = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
866
+ let count_ = count. clone ( ) ;
867
+ let mut config = Config :: default ( ) ;
868
+ config. dyn_import ( move |specifier, referrer| {
869
+ count_. fetch_add ( 1 , Ordering :: Relaxed ) ;
870
+ assert_eq ! ( specifier, "foo.js" ) ;
871
+ assert_eq ! ( referrer, "dyn_import1.js" ) ;
872
+ // We will never poll the isolate in this test, so just return a fake
873
+ // mod_id value.
874
+ Box :: new ( futures:: future:: ok ( 123 ) )
875
+ } ) ;
876
+ let mut isolate = Isolate :: new ( StartupData :: None , config) ;
877
+ js_check ( isolate. execute (
878
+ "dyn_import1.js" ,
879
+ r#"
880
+ (async () => {
881
+ await import("foo.js");
882
+ })();
883
+ "# ,
884
+ ) ) ;
885
+ assert_eq ! ( count. load( Ordering :: Relaxed ) , 1 ) ;
886
+ } )
887
+ }
888
+
889
+ #[ test]
890
+ fn test_dyn_import2 ( ) {
891
+ // Test an erroneous dynamic import where the specified module isn't found.
892
+ run_in_task ( || {
893
+ let count = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
894
+ let count_ = count. clone ( ) ;
895
+ let mut config = Config :: default ( ) ;
896
+ config. dyn_import ( move |specifier, referrer| {
897
+ count_. fetch_add ( 1 , Ordering :: Relaxed ) ;
898
+ assert_eq ! ( specifier, "foo.js" ) ;
899
+ assert_eq ! ( referrer, "dyn_import2.js" ) ;
900
+ Box :: new ( futures:: future:: err ( ( ) ) )
901
+ } ) ;
902
+ let mut isolate = Isolate :: new ( StartupData :: None , config) ;
903
+ js_check ( isolate. execute (
904
+ "dyn_import2.js" ,
905
+ r#"
906
+ (async () => {
907
+ await import("foo.js");
908
+ })();
909
+ "# ,
910
+ ) ) ;
911
+ assert_eq ! ( count. load( Ordering :: Relaxed ) , 1 ) ;
912
+
913
+ // We should get an error here.
914
+ let result = isolate. poll ( ) ;
915
+ assert ! ( result. is_err( ) ) ;
916
+ } )
917
+ }
918
+
919
+ #[ test]
920
+ fn test_dyn_import3 ( ) {
921
+ // Test an erroneous dynamic import where the specified module isn't found.
922
+ run_in_task ( || {
923
+ let count = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
924
+ let count_ = count. clone ( ) ;
925
+
926
+ // Sometimes Rust is really annoying.
927
+ use std:: sync:: Mutex ;
928
+ let mod_b = Arc :: new ( Mutex :: new ( 0 ) ) ;
929
+ let mod_b2 = mod_b. clone ( ) ;
930
+
931
+ let mut config = Config :: default ( ) ;
932
+ config. dyn_import ( move |_specifier, referrer| {
933
+ count_. fetch_add ( 1 , Ordering :: Relaxed ) ;
934
+ // assert_eq!(specifier, "foo.js");
935
+ assert_eq ! ( referrer, "dyn_import3.js" ) ;
936
+ let mod_id = mod_b2. lock ( ) . unwrap ( ) ;
937
+ Box :: new ( futures:: future:: ok ( * mod_id) )
938
+ } ) ;
939
+
940
+ let mut isolate = Isolate :: new ( StartupData :: None , config) ;
941
+
942
+ // Instantiate mod_b
943
+ {
944
+ let mut mod_id = mod_b. lock ( ) . unwrap ( ) ;
945
+ * mod_id = isolate
946
+ . mod_new ( false , "b.js" , "export function b() { return 'b' }" )
947
+ . unwrap ( ) ;
948
+ let mut resolve = move |_specifier : & str ,
949
+ _referrer : deno_mod |
950
+ -> deno_mod { unreachable ! ( ) } ;
951
+ js_check ( isolate. mod_instantiate ( * mod_id, & mut resolve) ) ;
952
+ }
953
+ // Dynamically import mod_b
954
+ js_check ( isolate. execute (
955
+ "dyn_import3.js" ,
956
+ r#"
957
+ (async () => {
958
+ let mod = await import("foo1.js");
959
+ if (mod.b() !== 'b') {
960
+ throw Error("bad1");
961
+ }
962
+ // And again!
963
+ mod = await import("foo2.js");
964
+ if (mod.b() !== 'b') {
965
+ throw Error("bad2");
966
+ }
967
+ })();
968
+ "# ,
969
+ ) ) ;
970
+ assert_eq ! ( count. load( Ordering :: Relaxed ) , 1 ) ;
971
+ assert_eq ! ( Ok ( Ready ( ( ) ) ) , isolate. poll( ) ) ;
972
+ } )
973
+ }
974
+
767
975
#[ test]
768
976
fn terminate_execution ( ) {
769
977
let ( tx, rx) = std:: sync:: mpsc:: channel :: < bool > ( ) ;
0 commit comments