1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-25 16:49:18 -05:00

feat: Allow "deno eval" to run code as module (#3148)

This commit is contained in:
Kevin (Kun) "Kassimo" Qian 2019-10-19 14:19:19 -07:00 committed by Ry Dahl
parent 4ae1838a6e
commit eff2a27bd0
4 changed files with 149 additions and 41 deletions

View file

@ -241,7 +241,7 @@ fn info_command(flags: DenoFlags, argv: Vec<String>) {
debug!("main_module {}", main_module); debug!("main_module {}", main_module);
worker worker
.execute_mod_async(&main_module, true) .execute_mod_async(&main_module, None, true)
.map_err(print_err_and_exit) .map_err(print_err_and_exit)
.and_then(move |()| print_file_info(worker, &main_module)) .and_then(move |()| print_file_info(worker, &main_module))
.and_then(|worker| { .and_then(|worker| {
@ -263,36 +263,41 @@ fn fetch_command(flags: DenoFlags, argv: Vec<String>) {
js_check(worker.execute("denoMain()")); js_check(worker.execute("denoMain()"));
debug!("main_module {}", main_module); debug!("main_module {}", main_module);
worker.execute_mod_async(&main_module, true).then(|result| { worker
js_check(result); .execute_mod_async(&main_module, None, true)
Ok(()) .then(|result| {
}) js_check(result);
Ok(())
})
}); });
tokio_util::run(main_future); tokio_util::run(main_future);
} }
fn eval_command(flags: DenoFlags, argv: Vec<String>) { fn eval_command(flags: DenoFlags, argv: Vec<String>) {
let (mut worker, state) = create_worker_and_state(flags, argv); let (mut worker, state) = create_worker_and_state(flags, argv);
// Wrap provided script in async function so asynchronous methods let ts_source = state.argv[1].clone();
// work. This is required until top-level await is not supported. // Force TypeScript compile.
let js_source = format!( let main_module =
"async function _topLevelWrapper(){{ ModuleSpecifier::resolve_url_or_path("./__$deno$eval.ts").unwrap();
{}
}}
_topLevelWrapper();
",
&state.argv[1]
);
let main_future = lazy(move || { let main_future = lazy(move || {
js_check(worker.execute("denoMain()")); js_check(worker.execute("denoMain()"));
// ATM imports in `deno eval` are not allowed debug!("main_module {}", &main_module);
// TODO Support ES modules once Worker supports evaluating anonymous modules.
js_check(worker.execute(&js_source)); let mut worker_ = worker.clone();
worker.then(|result| { worker
js_check(result); .execute_mod_async(&main_module, Some(ts_source), false)
Ok(()) .and_then(move |()| {
}) js_check(worker.execute("window.dispatchEvent(new Event('load'))"));
worker.then(move |result| {
js_check(result);
js_check(
worker_.execute("window.dispatchEvent(new Event('unload'))"),
);
Ok(())
})
})
.map_err(print_err_and_exit)
}); });
tokio_util::run(main_future); tokio_util::run(main_future);
} }
@ -356,7 +361,7 @@ fn run_script(flags: DenoFlags, argv: Vec<String>) {
let mut worker_ = worker.clone(); let mut worker_ = worker.clone();
worker worker
.execute_mod_async(&main_module, false) .execute_mod_async(&main_module, None, false)
.and_then(move |()| { .and_then(move |()| {
js_check(worker.execute("window.dispatchEvent(new Event('load'))")); js_check(worker.execute("window.dispatchEvent(new Event('load'))"));
worker.then(move |result| { worker.then(move |result| {

View file

@ -172,7 +172,7 @@ fn op_create_worker(
} }
let op = worker let op = worker
.execute_mod_async(&module_specifier, false) .execute_mod_async(&module_specifier, None, false)
.and_then(move |()| Ok(exec_cb(worker))); .and_then(move |()| Ok(exec_cb(worker)));
let result = op.wait()?; let result = op.wait()?;

View file

@ -91,15 +91,20 @@ impl Worker {
pub fn execute_mod_async( pub fn execute_mod_async(
&mut self, &mut self,
module_specifier: &ModuleSpecifier, module_specifier: &ModuleSpecifier,
maybe_code: Option<String>,
is_prefetch: bool, is_prefetch: bool,
) -> impl Future<Item = (), Error = ErrBox> { ) -> impl Future<Item = (), Error = ErrBox> {
let worker = self.clone(); let worker = self.clone();
let loader = self.state.clone(); let loader = self.state.clone();
let isolate = self.isolate.clone(); let isolate = self.isolate.clone();
let modules = self.state.modules.clone(); let modules = self.state.modules.clone();
let recursive_load = let recursive_load = RecursiveLoad::main(
RecursiveLoad::main(&module_specifier.to_string(), loader, modules) &module_specifier.to_string(),
.get_future(isolate); maybe_code,
loader,
modules,
)
.get_future(isolate);
recursive_load.and_then(move |id| -> Result<(), ErrBox> { recursive_load.and_then(move |id| -> Result<(), ErrBox> {
worker.state.progress.done(); worker.state.progress.done();
if is_prefetch { if is_prefetch {
@ -156,7 +161,7 @@ mod tests {
let mut worker = let mut worker =
Worker::new("TEST".to_string(), StartupData::None, state); Worker::new("TEST".to_string(), StartupData::None, state);
worker worker
.execute_mod_async(&module_specifier, false) .execute_mod_async(&module_specifier, None, false)
.then(|result| { .then(|result| {
if let Err(err) = result { if let Err(err) = result {
eprintln!("execute_mod err {:?}", err); eprintln!("execute_mod err {:?}", err);
@ -193,7 +198,7 @@ mod tests {
let mut worker = let mut worker =
Worker::new("TEST".to_string(), StartupData::None, state); Worker::new("TEST".to_string(), StartupData::None, state);
worker worker
.execute_mod_async(&module_specifier, false) .execute_mod_async(&module_specifier, None, false)
.then(|result| { .then(|result| {
if let Err(err) = result { if let Err(err) = result {
eprintln!("execute_mod err {:?}", err); eprintln!("execute_mod err {:?}", err);
@ -233,7 +238,7 @@ mod tests {
); );
worker.execute("denoMain()").unwrap(); worker.execute("denoMain()").unwrap();
worker worker
.execute_mod_async(&module_specifier, false) .execute_mod_async(&module_specifier, None, false)
.then(|result| { .then(|result| {
if let Err(err) = result { if let Err(err) = result {
eprintln!("execute_mod err {:?}", err); eprintln!("execute_mod err {:?}", err);
@ -354,7 +359,9 @@ mod tests {
let mut worker = create_test_worker(); let mut worker = create_test_worker();
let module_specifier = let module_specifier =
ModuleSpecifier::resolve_url_or_path("does-not-exist").unwrap(); ModuleSpecifier::resolve_url_or_path("does-not-exist").unwrap();
let result = worker.execute_mod_async(&module_specifier, false).wait(); let result = worker
.execute_mod_async(&module_specifier, None, false)
.wait();
assert!(result.is_err()); assert!(result.is_err());
}) })
} }
@ -372,7 +379,9 @@ mod tests {
.to_owned(); .to_owned();
let module_specifier = let module_specifier =
ModuleSpecifier::resolve_url_or_path(&p.to_string_lossy()).unwrap(); ModuleSpecifier::resolve_url_or_path(&p.to_string_lossy()).unwrap();
let result = worker.execute_mod_async(&module_specifier, false).wait(); let result = worker
.execute_mod_async(&module_specifier, None, false)
.wait();
assert!(result.is_ok()); assert!(result.is_ok());
}) })
} }

View file

@ -58,8 +58,8 @@ enum Kind {
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
enum State { enum State {
ResolveMain(String), // specifier ResolveMain(String, Option<String>), // specifier, maybe code
ResolveImport(String, String), // specifier, referrer ResolveImport(String, String), // specifier, referrer
LoadingRoot, LoadingRoot,
LoadingImports(deno_mod), LoadingImports(deno_mod),
Instantiated(deno_mod), Instantiated(deno_mod),
@ -81,11 +81,12 @@ impl<L: Loader> RecursiveLoad<L> {
/// Starts a new parallel load of the given URL of the main module. /// Starts a new parallel load of the given URL of the main module.
pub fn main( pub fn main(
specifier: &str, specifier: &str,
code: Option<String>,
loader: L, loader: L,
modules: Arc<Mutex<Modules>>, modules: Arc<Mutex<Modules>>,
) -> Self { ) -> Self {
let kind = Kind::Main; let kind = Kind::Main;
let state = State::ResolveMain(specifier.to_owned()); let state = State::ResolveMain(specifier.to_owned(), code);
Self::new(kind, state, loader, modules) Self::new(kind, state, loader, modules)
} }
@ -126,7 +127,7 @@ impl<L: Loader> RecursiveLoad<L> {
fn add_root(&mut self) -> Result<(), ErrBox> { fn add_root(&mut self) -> Result<(), ErrBox> {
let module_specifier = match self.state { let module_specifier = match self.state {
State::ResolveMain(ref specifier) => self.loader.resolve( State::ResolveMain(ref specifier, _) => self.loader.resolve(
specifier, specifier,
".", ".",
true, true,
@ -313,6 +314,21 @@ impl<L: Loader> Stream for RecursiveLoad<L> {
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
Ok(match self.state { Ok(match self.state {
State::ResolveMain(ref specifier, Some(ref code)) => {
let module_specifier = self.loader.resolve(
specifier,
".",
true,
self.dyn_import_id().is_some(),
)?;
let info = SourceCodeInfo {
code: code.to_owned(),
module_url_specified: module_specifier.to_string(),
module_url_found: module_specifier.to_string(),
};
self.state = State::LoadingRoot;
Ready(Some(Event::Fetch(info)))
}
State::ResolveMain(..) | State::ResolveImport(..) => { State::ResolveMain(..) | State::ResolveImport(..) => {
self.add_root()?; self.add_root()?;
self.poll()? self.poll()?
@ -630,6 +646,8 @@ mod tests {
} }
"/main.js" => Some((MAIN_SRC, "file:///main.js")), "/main.js" => Some((MAIN_SRC, "file:///main.js")),
"/bad_import.js" => Some((BAD_IMPORT_SRC, "file:///bad_import.js")), "/bad_import.js" => Some((BAD_IMPORT_SRC, "file:///bad_import.js")),
// deliberately empty code.
"/main_with_code.js" => Some(("", "file:///main_with_code.js")),
_ => None, _ => None,
} }
} }
@ -769,7 +787,8 @@ mod tests {
let isolate = loader.isolate.clone(); let isolate = loader.isolate.clone();
let isolate_ = isolate.clone(); let isolate_ = isolate.clone();
let loads = loader.loads.clone(); let loads = loader.loads.clone();
let mut recursive_load = RecursiveLoad::main("/a.js", loader, modules); let mut recursive_load =
RecursiveLoad::main("/a.js", None, loader, modules);
let a_id = loop { let a_id = loop {
match recursive_load.poll() { match recursive_load.poll() {
@ -848,7 +867,7 @@ mod tests {
let modules_ = modules.clone(); let modules_ = modules.clone();
let loads = loader.loads.clone(); let loads = loader.loads.clone();
let recursive_load = let recursive_load =
RecursiveLoad::main("/circular1.js", loader, modules); RecursiveLoad::main("/circular1.js", None, loader, modules);
let result = recursive_load.get_future(isolate.clone()).poll(); let result = recursive_load.get_future(isolate.clone()).poll();
assert!(result.is_ok()); assert!(result.is_ok());
if let Async::Ready(circular1_id) = result.ok().unwrap() { if let Async::Ready(circular1_id) = result.ok().unwrap() {
@ -919,7 +938,7 @@ mod tests {
let modules_ = modules.clone(); let modules_ = modules.clone();
let loads = loader.loads.clone(); let loads = loader.loads.clone();
let recursive_load = let recursive_load =
RecursiveLoad::main("/redirect1.js", loader, modules); RecursiveLoad::main("/redirect1.js", None, loader, modules);
let result = recursive_load.get_future(isolate.clone()).poll(); let result = recursive_load.get_future(isolate.clone()).poll();
println!(">> result {:?}", result); println!(">> result {:?}", result);
assert!(result.is_ok()); assert!(result.is_ok());
@ -982,7 +1001,8 @@ mod tests {
let modules = loader.modules.clone(); let modules = loader.modules.clone();
let loads = loader.loads.clone(); let loads = loader.loads.clone();
let mut recursive_load = let mut recursive_load =
RecursiveLoad::main("/main.js", loader, modules).get_future(isolate); RecursiveLoad::main("/main.js", None, loader, modules)
.get_future(isolate);
let result = recursive_load.poll(); let result = recursive_load.poll();
assert!(result.is_ok()); assert!(result.is_ok());
@ -1030,7 +1050,7 @@ mod tests {
let isolate = loader.isolate.clone(); let isolate = loader.isolate.clone();
let modules = loader.modules.clone(); let modules = loader.modules.clone();
let recursive_load = let recursive_load =
RecursiveLoad::main("/bad_import.js", loader, modules); RecursiveLoad::main("/bad_import.js", None, loader, modules);
let result = recursive_load.get_future(isolate).poll(); let result = recursive_load.get_future(isolate).poll();
assert!(result.is_err()); assert!(result.is_err());
let err = result.err().unwrap(); let err = result.err().unwrap();
@ -1041,6 +1061,80 @@ mod tests {
}) })
} }
const MAIN_WITH_CODE_SRC: &str = r#"
import { b } from "/b.js";
import { c } from "/c.js";
if (b() != 'b') throw Error();
if (c() != 'c') throw Error();
if (!import.meta.main) throw Error();
if (import.meta.url != 'file:///main_with_code.js') throw Error();
"#;
#[test]
fn recursive_load_main_with_code() {
run_in_task(|| {
let loader = MockLoader::new();
let modules = loader.modules.clone();
let modules_ = modules.clone();
let isolate = loader.isolate.clone();
let isolate_ = isolate.clone();
let loads = loader.loads.clone();
// In default resolution code should be empty.
// Instead we explicitly pass in our own code.
// The behavior should be very similar to /a.js.
let mut recursive_load = RecursiveLoad::main(
"/main_with_code.js",
Some(MAIN_WITH_CODE_SRC.to_owned()),
loader,
modules,
);
let main_id = loop {
match recursive_load.poll() {
Ok(Ready(Some(Event::Fetch(info)))) => {
let mut isolate = isolate.lock().unwrap();
recursive_load.register(info, &mut isolate).unwrap();
}
Ok(Ready(Some(Event::Instantiate(id)))) => break id,
_ => panic!("unexpected result"),
};
};
let mut isolate = isolate_.lock().unwrap();
js_check(isolate.mod_evaluate(main_id));
let l = loads.lock().unwrap();
assert_eq!(
l.to_vec(),
vec!["file:///b.js", "file:///c.js", "file:///d.js"]
);
let modules = modules_.lock().unwrap();
assert_eq!(modules.get_id("file:///main_with_code.js"), Some(main_id));
let b_id = modules.get_id("file:///b.js").unwrap();
let c_id = modules.get_id("file:///c.js").unwrap();
let d_id = modules.get_id("file:///d.js").unwrap();
assert_eq!(
modules.get_children(main_id),
Some(&vec![
"file:///b.js".to_string(),
"file:///c.js".to_string()
])
);
assert_eq!(
modules.get_children(b_id),
Some(&vec!["file:///c.js".to_string()])
);
assert_eq!(
modules.get_children(c_id),
Some(&vec!["file:///d.js".to_string()])
);
assert_eq!(modules.get_children(d_id), Some(&vec![]));
})
}
#[test] #[test]
fn empty_deps() { fn empty_deps() {
let modules = Modules::new(); let modules = Modules::new();