0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-02-02 04:38:21 -05:00

refactor(cli/repl): tightly integrate event loop (#7834)

This commit is contained in:
Casper Beyer 2020-10-06 19:50:48 +08:00 committed by GitHub
parent 6b8aef5103
commit 21965e8a96
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 134 additions and 86 deletions

View file

@ -63,7 +63,6 @@ use crate::file_fetcher::SourceFileFetcher;
use crate::file_fetcher::TextDocument; use crate::file_fetcher::TextDocument;
use crate::fs as deno_fs; use crate::fs as deno_fs;
use crate::global_state::GlobalState; use crate::global_state::GlobalState;
use crate::inspector::InspectorSession;
use crate::media_type::MediaType; use crate::media_type::MediaType;
use crate::permissions::Permissions; use crate::permissions::Permissions;
use crate::worker::MainWorker; use crate::worker::MainWorker;
@ -432,26 +431,7 @@ async fn run_repl(flags: Flags) -> Result<(), AnyError> {
let mut worker = MainWorker::new(&global_state, main_module.clone()); let mut worker = MainWorker::new(&global_state, main_module.clone());
(&mut *worker).await?; (&mut *worker).await?;
let inspector = worker repl::run(&global_state, worker).await
.inspector
.as_mut()
.expect("Inspector is not created.");
let inspector_session = InspectorSession::new(&mut **inspector);
let repl = repl::run(&global_state, inspector_session);
tokio::pin!(repl);
loop {
tokio::select! {
result = &mut repl => {
return result;
}
_ = &mut *worker => {
tokio::time::delay_for(tokio::time::Duration::from_millis(10)).await;
}
}
}
} }
async fn run_from_stdin(flags: Flags) -> Result<(), AnyError> { async fn run_from_stdin(flags: Flags) -> Result<(), AnyError> {

View file

@ -2,8 +2,11 @@
use crate::global_state::GlobalState; use crate::global_state::GlobalState;
use crate::inspector::InspectorSession; use crate::inspector::InspectorSession;
use crate::worker::MainWorker;
use crate::worker::Worker;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::serde_json::json; use deno_core::serde_json::json;
use deno_core::serde_json::Value;
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
use rustyline::validate::MatchingBracketValidator; use rustyline::validate::MatchingBracketValidator;
use rustyline::validate::ValidationContext; use rustyline::validate::ValidationContext;
@ -29,17 +32,76 @@ impl Validator for Helper {
} }
} }
async fn post_message_and_poll(
worker: &mut Worker,
session: &mut InspectorSession,
method: &str,
params: Option<Value>,
) -> Result<Value, AnyError> {
let response = session.post_message(method.to_string(), params);
tokio::pin!(response);
loop {
tokio::select! {
result = &mut response => {
return result
}
_ = &mut *worker => {
// A zero delay is long enough to yield the thread in order to prevent the loop from
// running hot for messages that are taking longer to resolve like for example an
// evaluation of top level await.
tokio::time::delay_for(tokio::time::Duration::from_millis(0)).await;
}
}
}
}
async fn read_line_and_poll(
worker: &mut Worker,
editor: Arc<Mutex<Editor<Helper>>>,
) -> Result<String, ReadlineError> {
let mut line =
tokio::task::spawn_blocking(move || editor.lock().unwrap().readline("> "));
let mut poll_worker = true;
loop {
// Because an inspector websocket client may choose to connect at anytime when we have an
// inspector server we need to keep polling the worker to pick up new connections.
let mut timeout =
tokio::time::delay_for(tokio::time::Duration::from_millis(1000));
tokio::select! {
result = &mut line => {
return result.unwrap();
}
_ = &mut *worker, if poll_worker => {
poll_worker = false;
}
_ = &mut timeout => {
poll_worker = true
}
}
}
}
pub async fn run( pub async fn run(
global_state: &GlobalState, global_state: &GlobalState,
mut session: Box<InspectorSession>, mut worker: MainWorker,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
// Our inspector is unable to default to the default context id so we have to specify it here. // Our inspector is unable to default to the default context id so we have to specify it here.
let context_id: u32 = 1; let context_id: u32 = 1;
let inspector = worker
.inspector
.as_mut()
.expect("Inspector is not created.");
let mut session = InspectorSession::new(&mut **inspector);
let history_file = global_state.dir.root.join("deno_history.txt"); let history_file = global_state.dir.root.join("deno_history.txt");
session post_message_and_poll(&mut *worker, &mut session, "Runtime.enable", None)
.post_message("Runtime.enable".to_string(), None)
.await?; .await?;
let helper = Helper { let helper = Helper {
@ -90,9 +152,10 @@ pub async fn run(
}); });
"#; "#;
session post_message_and_poll(
.post_message( &mut *worker,
"Runtime.evaluate".to_string(), &mut session,
"Runtime.evaluate",
Some(json!({ Some(json!({
"expression": prelude, "expression": prelude,
"contextId": context_id, "contextId": context_id,
@ -101,12 +164,7 @@ pub async fn run(
.await?; .await?;
loop { loop {
let editor2 = editor.clone(); let line = read_line_and_poll(&mut *worker, editor.clone()).await;
let line = tokio::task::spawn_blocking(move || {
editor2.lock().unwrap().readline("> ")
})
.await?;
match line { match line {
Ok(line) => { Ok(line) => {
// It is a bit unexpected that { "foo": "bar" } is interpreted as a block // It is a bit unexpected that { "foo": "bar" } is interpreted as a block
@ -120,9 +178,10 @@ pub async fn run(
line.clone() line.clone()
}; };
let evaluate_response = session let evaluate_response = post_message_and_poll(
.post_message( &mut *worker,
"Runtime.evaluate".to_string(), &mut session,
"Runtime.evaluate",
Some(json!({ Some(json!({
"expression": format!("'use strict'; void 0;\n{}", &wrapped_line), "expression": format!("'use strict'; void 0;\n{}", &wrapped_line),
"contextId": context_id, "contextId": context_id,
@ -137,9 +196,10 @@ pub async fn run(
if evaluate_response.get("exceptionDetails").is_some() if evaluate_response.get("exceptionDetails").is_some()
&& wrapped_line != line && wrapped_line != line
{ {
session post_message_and_poll(
.post_message( &mut *worker,
"Runtime.evaluate".to_string(), &mut session,
"Runtime.evaluate",
Some(json!({ Some(json!({
"expression": format!("'use strict'; void 0;\n{}", &line), "expression": format!("'use strict'; void 0;\n{}", &line),
"contextId": context_id, "contextId": context_id,
@ -151,9 +211,10 @@ pub async fn run(
evaluate_response evaluate_response
}; };
let is_closing = session let is_closing = post_message_and_poll(
.post_message( &mut *worker,
"Runtime.evaluate".to_string(), &mut session,
"Runtime.evaluate",
Some(json!({ Some(json!({
"expression": "(globalThis.closed)", "expression": "(globalThis.closed)",
"contextId": context_id, "contextId": context_id,
@ -176,42 +237,49 @@ pub async fn run(
evaluate_response.get("exceptionDetails"); evaluate_response.get("exceptionDetails");
if evaluate_exception_details.is_some() { if evaluate_exception_details.is_some() {
session post_message_and_poll(
.post_message( &mut *worker,
"Runtime.callFunctionOn".to_string(), &mut session,
"Runtime.callFunctionOn",
Some(json!({ Some(json!({
"executionContextId": context_id, "executionContextId": context_id,
"functionDeclaration": "function (object) { Deno[Deno.internal].lastThrownError = object; }", "functionDeclaration": "function (object) { Deno[Deno.internal].lastThrownError = object; }",
"arguments": [ "arguments": [
evaluate_result, evaluate_result,
], ],
}))).await?; })),
).await?;
} else { } else {
session post_message_and_poll(
.post_message( &mut *worker,
"Runtime.callFunctionOn".to_string(), &mut session,
"Runtime.callFunctionOn",
Some(json!({ Some(json!({
"executionContextId": context_id, "executionContextId": context_id,
"functionDeclaration": "function (object) { Deno[Deno.internal].lastEvalResult = object; }", "functionDeclaration": "function (object) { Deno[Deno.internal].lastEvalResult = object; }",
"arguments": [ "arguments": [
evaluate_result, evaluate_result,
], ],
}))).await?; })),
).await?;
} }
// TODO(caspervonb) we should investigate using previews here but to keep things // TODO(caspervonb) we should investigate using previews here but to keep things
// consistent with the previous implementation we just get the preview result from // consistent with the previous implementation we just get the preview result from
// Deno.inspectArgs. // Deno.inspectArgs.
let inspect_response = session let inspect_response =
.post_message( post_message_and_poll(
"Runtime.callFunctionOn".to_string(), &mut *worker,
&mut session,
"Runtime.callFunctionOn",
Some(json!({ Some(json!({
"executionContextId": context_id, "executionContextId": context_id,
"functionDeclaration": "function (object) { return Deno[Deno.internal].inspectArgs(['%o', object], { colors: true}); }", "functionDeclaration": "function (object) { return Deno[Deno.internal].inspectArgs(['%o', object], { colors: true}); }",
"arguments": [ "arguments": [
evaluate_result, evaluate_result,
], ],
}))).await?; })),
).await?;
let inspect_result = inspect_response.get("result").unwrap(); let inspect_result = inspect_response.get("result").unwrap();