From 4799aaac15285833341d1e0471a2559bd325982f Mon Sep 17 00:00:00 2001 From: Leo Kettmeir Date: Mon, 7 Feb 2022 17:05:49 +0100 Subject: [PATCH] refactor: factor out CDP message types (#13551) --- cli/cdp.rs | 414 ++++++++++++++++++++++++++++ cli/main.rs | 1 + cli/tests/integration/repl_tests.rs | 17 ++ cli/tools/coverage/mod.rs | 29 +- cli/tools/repl/channel.rs | 9 +- cli/tools/repl/editor.rs | 81 +++--- cli/tools/repl/mod.rs | 5 +- cli/tools/repl/session.rs | 148 ++++++---- core/inspector.rs | 4 +- test_util/src/pty.rs | 2 +- 10 files changed, 599 insertions(+), 111 deletions(-) create mode 100644 cli/cdp.rs diff --git a/cli/cdp.rs b/cli/cdp.rs new file mode 100644 index 0000000000..55229947db --- /dev/null +++ b/cli/cdp.rs @@ -0,0 +1,414 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +/// https://chromedevtools.github.io/devtools-protocol/tot/ +use deno_core::serde_json; +use deno_core::serde_json::Value; +use serde::Deserialize; +use serde::Serialize; + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-awaitPromise +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AwaitPromiseArgs { + pub promise_object_id: RemoteObjectId, + #[serde(skip_serializing_if = "Option::is_none")] + pub return_by_value: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub generate_preview: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-awaitPromise +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AwaitPromiseResponse { + pub result: RemoteObject, + pub exception_details: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-callFunctionOn +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CallFunctionOnArgs { + pub function_declaration: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub object_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub arguments: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub silent: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub return_by_value: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub generate_preview: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub user_gesture: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub await_promise: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub execution_context_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub object_group: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub throw_on_side_effect: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-callFunctionOn +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CallFunctionOnResponse { + pub result: RemoteObject, + pub exception_details: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-compileScript +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CompileScriptArgs { + pub expression: String, + #[serde(rename = "sourceURL")] + pub source_url: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub execution_context_id: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-compileScript +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CompileScriptResponse { + pub script_id: Option, + pub exception_details: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-evaluate +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct EvaluateArgs { + pub expression: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub object_group: Option, + #[serde( + rename = "includeCommandLineAPI", + skip_serializing_if = "Option::is_none" + )] + pub include_command_line_api: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub silent: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub context_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub return_by_value: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub generate_preview: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub user_gesture: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub await_promise: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub throw_on_side_effect: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub timeout: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub disable_breaks: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub repl_mode: Option, + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(rename = "allowUnsafeEvalBlockedByCSP")] + pub allow_unsafe_eval_blocked_by_csp: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub unique_context_id: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-evaluate +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct EvaluateResponse { + pub result: RemoteObject, + pub exception_details: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-getProperties +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct GetPropertiesArgs { + pub object_id: RemoteObjectId, + #[serde(skip_serializing_if = "Option::is_none")] + pub own_properties: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub accessor_properties_only: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub generate_preview: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub non_indexed_properties_only: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-getProperties +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetPropertiesResponse { + pub result: Vec, + pub internal_properties: Option>, + pub private_properties: Option>, + pub exception_details: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-globalLexicalScopeNames +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct GlobalLexicalScopeNamesArgs { + #[serde(skip_serializing_if = "Option::is_none")] + pub execution_context_id: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-globalLexicalScopeNames +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GlobalLexicalScopeNamesResponse { + pub names: Vec, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-queryObjects +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct QueryObjectsArgs { + pub prototype_object_id: RemoteObjectId, + #[serde(skip_serializing_if = "Option::is_none")] + pub object_group: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-queryObjects +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct QueryObjectsResponse { + pub objects: RemoteObject, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-releaseObject +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ReleaseObjectArgs { + pub object_id: RemoteObjectId, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-releaseObjectGroup +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ReleaseObjectGroupArgs { + pub object_group: String, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-runScript +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RunScriptArgs { + pub script_id: ScriptId, + #[serde(skip_serializing_if = "Option::is_none")] + pub execution_context_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub object_group: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub silent: Option, + #[serde( + rename = "includeCommandLineAPI", + skip_serializing_if = "Option::is_none" + )] + pub include_command_line_api: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub return_by_value: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub generate_preview: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub await_promise: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-runScript +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RunScriptResponse { + pub result: RemoteObject, + pub exception_details: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-setAsyncCallStackDepth +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SetAsyncCallStackDepthArgs { + pub max_depth: u64, +} + +// types + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObject +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RemoteObject { + #[serde(rename = "type")] + pub kind: String, + pub subtype: Option, + pub class_name: Option, + pub value: Option, + pub unserializable_value: Option, + pub description: Option, + pub object_id: Option, + pub preview: Option, + pub custom_preview: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-ObjectPreview +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ObjectPreview { + #[serde(rename = "type")] + pub kind: String, + pub subtype: Option, + pub description: Option, + pub overflow: bool, + pub properties: Vec, + pub entries: Option>, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-PropertyPreview +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PropertyPreview { + pub name: String, + #[serde(rename = "type")] + pub kind: String, + pub value: Option, + pub value_preview: Option, + pub subtype: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-EntryPreview +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct EntryPreview { + pub key: Option, + pub value: ObjectPreview, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-CustomPreview +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CustomPreview { + pub header: String, + pub body_getter_id: RemoteObjectId, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-ExceptionDetails +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ExceptionDetails { + pub exception_id: u64, + pub text: String, + pub line_number: u64, + pub column_number: u64, + pub script_id: Option, + pub url: Option, + pub stack_trace: Option, + pub exception: Option, + pub execution_context_id: Option, + pub exception_meta_data: Option>, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-StackTrace +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct StackTrace { + pub description: Option, + pub call_frames: Vec, + pub parent: Option>, + pub parent_id: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-CallFrame +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CallFrame { + pub function_name: String, + pub script_id: ScriptId, + pub url: String, + pub line_number: u64, + pub column_number: u64, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-StackTraceId +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct StackTraceId { + pub id: String, + pub debugger_id: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-CallArgument +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CallArgument { + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub unserializable_value: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub object_id: Option, +} + +impl From<&RemoteObject> for CallArgument { + fn from(obj: &RemoteObject) -> Self { + Self { + value: obj.value.clone(), + unserializable_value: obj.unserializable_value.clone(), + object_id: obj.object_id.clone(), + } + } +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-InternalPropertyDescriptor +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PropertyDescriptor { + pub name: String, + pub value: Option, + pub writable: Option, + pub get: Option, + pub set: Option, + pub configurable: bool, + pub enumerable: bool, + pub was_thrown: Option, + pub is_own: Option, + pub symbol: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-InternalPropertyDescriptor +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct InternalPropertyDescriptor { + pub name: String, + pub value: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-PrivatePropertyDescriptor +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PrivatePropertyDescriptor { + pub name: String, + pub value: Option, + pub get: Option, + pub set: Option, +} + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObjectId +pub type RemoteObjectId = String; + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-ExecutionContextId +pub type ExecutionContextId = u64; + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-ScriptId +pub type ScriptId = String; + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-TimeDelta +pub type TimeDelta = u64; + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-UnserializableValue +pub type UnserializableValue = String; + +/// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-UniqueDebuggerId +pub type UniqueDebuggerId = String; diff --git a/cli/main.rs b/cli/main.rs index 0e9a57382c..ecadf65bdd 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -2,6 +2,7 @@ mod auth_tokens; mod cache; +mod cdp; mod checksum; mod compat; mod config_file; diff --git a/cli/tests/integration/repl_tests.rs b/cli/tests/integration/repl_tests.rs index 68379c6951..571b65fa24 100644 --- a/cli/tests/integration/repl_tests.rs +++ b/cli/tests/integration/repl_tests.rs @@ -122,6 +122,23 @@ fn pty_complete_primitives() { }); } +#[test] +fn pty_complete_expression() { + util::with_pty(&["repl"], |mut console| { + console.write_text("Deno.\t\t"); + console.write_text("y"); + console.write_line(""); + console.write_line("close();"); + let output = console.read_all_output(); + assert!(output.contains("Display all")); + assert!(output.contains("core")); + assert!(output.contains("args")); + assert!(output.contains("exit")); + assert!(output.contains("symlink")); + assert!(output.contains("permissions")); + }); +} + #[test] fn pty_complete_imports() { util::with_pty(&["repl"], |mut console| { diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index 1c14859ea9..6177295a66 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -43,22 +43,34 @@ impl CoverageCollector { } async fn enable_debugger(&mut self) -> Result<(), AnyError> { - self.session.post_message("Debugger.enable", None).await?; + self + .session + .post_message::<()>("Debugger.enable", None) + .await?; Ok(()) } async fn enable_profiler(&mut self) -> Result<(), AnyError> { - self.session.post_message("Profiler.enable", None).await?; + self + .session + .post_message::<()>("Profiler.enable", None) + .await?; Ok(()) } async fn disable_debugger(&mut self) -> Result<(), AnyError> { - self.session.post_message("Debugger.disable", None).await?; + self + .session + .post_message::<()>("Debugger.disable", None) + .await?; Ok(()) } async fn disable_profiler(&mut self) -> Result<(), AnyError> { - self.session.post_message("Profiler.disable", None).await?; + self + .session + .post_message::<()>("Profiler.disable", None) + .await?; Ok(()) } @@ -66,10 +78,9 @@ impl CoverageCollector { &mut self, parameters: StartPreciseCoverageParameters, ) -> Result { - let parameters_value = serde_json::to_value(parameters)?; let return_value = self .session - .post_message("Profiler.startPreciseCoverage", Some(parameters_value)) + .post_message("Profiler.startPreciseCoverage", Some(parameters)) .await?; let return_object = serde_json::from_value(return_value)?; @@ -82,7 +93,7 @@ impl CoverageCollector { ) -> Result { let return_value = self .session - .post_message("Profiler.takePreciseCoverage", None) + .post_message::<()>("Profiler.takePreciseCoverage", None) .await?; let return_object = serde_json::from_value(return_value)?; @@ -592,8 +603,8 @@ pub async fn cover_files( })? }; let file = maybe_file.ok_or_else(|| { - anyhow!("Failed to fetch \"{}\" from cache. - Before generating coverage report, run `deno test --coverage` to ensure consistent state.", + anyhow!("Failed to fetch \"{}\" from cache. + Before generating coverage report, run `deno test --coverage` to ensure consistent state.", module_specifier ) })?; diff --git a/cli/tools/repl/channel.rs b/cli/tools/repl/channel.rs index 7cc8029057..4f2086fb5c 100644 --- a/cli/tools/repl/channel.rs +++ b/cli/tools/repl/channel.rs @@ -2,6 +2,7 @@ use deno_core::anyhow::anyhow; use deno_core::error::AnyError; +use deno_core::serde_json; use deno_core::serde_json::Value; use std::cell::RefCell; use tokio::sync::mpsc::channel; @@ -55,17 +56,19 @@ pub struct RustylineSyncMessageSender { } impl RustylineSyncMessageSender { - pub fn post_message( + pub fn post_message( &self, method: &str, - params: Option, + params: Option, ) -> Result { if let Err(err) = self .message_tx .blocking_send(RustylineSyncMessage::PostMessage { method: method.to_string(), - params, + params: params + .map(|params| serde_json::to_value(params)) + .transpose()?, }) { Err(anyhow!("{}", err)) diff --git a/cli/tools/repl/editor.rs b/cli/tools/repl/editor.rs index 30b9fba87b..ea62bd49ff 100644 --- a/cli/tools/repl/editor.rs +++ b/cli/tools/repl/editor.rs @@ -1,13 +1,13 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +use crate::cdp; use crate::colors; use deno_ast::swc::parser::error::SyntaxError; use deno_ast::swc::parser::token::Token; use deno_ast::swc::parser::token::Word; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; -use deno_core::serde_json::json; -use deno_core::serde_json::Value; +use deno_core::serde_json; use rustyline::completion::Completer; use rustyline::error::ReadlineError; use rustyline::highlight::Highlighter; @@ -39,20 +39,14 @@ impl EditorHelper { .sync_sender .post_message( "Runtime.globalLexicalScopeNames", - Some(json!({ - "executionContextId": self.context_id, - })), + Some(cdp::GlobalLexicalScopeNamesArgs { + execution_context_id: Some(self.context_id), + }), ) .unwrap(); - - evaluate_response - .get("names") - .unwrap() - .as_array() - .unwrap() - .iter() - .map(|n| n.as_str().unwrap().to_string()) - .collect() + let evaluate_response: cdp::GlobalLexicalScopeNamesResponse = + serde_json::from_value(evaluate_response).unwrap(); + evaluate_response.names } pub fn get_expression_property_names(&self, expr: &str) -> Vec { @@ -80,12 +74,7 @@ impl EditorHelper { } fn get_expression_type(&self, expr: &str) -> Option { - self - .evaluate_expression(expr)? - .get("result")? - .get("type")? - .as_str() - .map(|s| s.to_string()) + self.evaluate_expression(expr).map(|res| res.result.kind) } fn get_object_expr_properties( @@ -93,44 +82,60 @@ impl EditorHelper { object_expr: &str, ) -> Option> { let evaluate_result = self.evaluate_expression(object_expr)?; - let object_id = evaluate_result.get("result")?.get("objectId")?; + let object_id = evaluate_result.result.object_id?; let get_properties_response = self .sync_sender .post_message( "Runtime.getProperties", - Some(json!({ - "objectId": object_id, - })), + Some(cdp::GetPropertiesArgs { + object_id, + own_properties: None, + accessor_properties_only: None, + generate_preview: None, + non_indexed_properties_only: None, + }), ) .ok()?; - + let get_properties_response: cdp::GetPropertiesResponse = + serde_json::from_value(get_properties_response).ok()?; Some( get_properties_response - .get("result")? - .as_array() - .unwrap() - .iter() - .map(|r| r.get("name").unwrap().as_str().unwrap().to_string()) + .result + .into_iter() + .map(|prop| prop.name) .collect(), ) } - fn evaluate_expression(&self, expr: &str) -> Option { + fn evaluate_expression(&self, expr: &str) -> Option { let evaluate_response = self .sync_sender .post_message( "Runtime.evaluate", - Some(json!({ - "contextId": self.context_id, - "expression": expr, - "throwOnSideEffect": true, - "timeout": 200, - })), + Some(cdp::EvaluateArgs { + expression: expr.to_string(), + object_group: None, + include_command_line_api: None, + silent: None, + context_id: Some(self.context_id), + return_by_value: None, + generate_preview: None, + user_gesture: None, + await_promise: None, + throw_on_side_effect: Some(true), + timeout: Some(200), + disable_breaks: None, + repl_mode: None, + allow_unsafe_eval_blocked_by_csp: None, + unique_context_id: None, + }), ) .ok()?; + let evaluate_response: cdp::EvaluateResponse = + serde_json::from_value(evaluate_response).ok()?; - if evaluate_response.get("exceptionDetails").is_some() { + if evaluate_response.exception_details.is_some() { None } else { Some(evaluate_response) diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs index 0c9fdbfb0c..b49c641c48 100644 --- a/cli/tools/repl/mod.rs +++ b/cli/tools/repl/mod.rs @@ -33,10 +33,7 @@ async fn read_line_and_poll( } result = message_handler.recv() => { match result { - Some(RustylineSyncMessage::PostMessage { - method, - params - }) => { + Some(RustylineSyncMessage::PostMessage { method, params }) => { let result = repl_session .post_message_with_event_loop(&method, params) .await; diff --git a/cli/tools/repl/session.rs b/cli/tools/repl/session.rs index 5d458189f9..57109eb1d0 100644 --- a/cli/tools/repl/session.rs +++ b/cli/tools/repl/session.rs @@ -1,12 +1,13 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +use crate::cdp; use crate::colors; use crate::lsp::ReplLanguageServer; use deno_ast::DiagnosticsError; use deno_ast::ImportsNotUsedAsValues; use deno_core::error::AnyError; use deno_core::futures::FutureExt; -use deno_core::serde_json::json; +use deno_core::serde_json; use deno_core::serde_json::Value; use deno_core::LocalInspectorSession; use deno_runtime::worker::MainWorker; @@ -58,7 +59,7 @@ impl std::fmt::Display for EvaluationOutput { struct TsEvaluateResponse { ts_code: String, - value: Value, + value: cdp::EvaluateResponse, } pub struct ReplSession { @@ -75,7 +76,9 @@ impl ReplSession { worker .with_event_loop( - session.post_message("Runtime.enable", None).boxed_local(), + session + .post_message::<()>("Runtime.enable", None) + .boxed_local(), ) .await?; @@ -115,9 +118,8 @@ impl ReplSession { let closed = self .evaluate_expression("(this.closed)") .await? - .get("result") - .unwrap() - .get("value") + .result + .value .unwrap() .as_bool() .unwrap(); @@ -125,10 +127,10 @@ impl ReplSession { Ok(closed) } - pub async fn post_message_with_event_loop( + pub async fn post_message_with_event_loop( &mut self, method: &str, - params: Option, + params: Option, ) -> Result { self .worker @@ -156,23 +158,24 @@ impl ReplSession { match self.evaluate_line_with_object_wrapping(line).await { Ok(evaluate_response) => { - let evaluate_result = evaluate_response.value.get("result").unwrap(); - let evaluate_exception_details = - evaluate_response.value.get("exceptionDetails"); + let cdp::EvaluateResponse { + result, + exception_details, + } = evaluate_response.value; - if evaluate_exception_details.is_some() { - self.set_last_thrown_error(evaluate_result).await?; + if exception_details.is_some() { + self.set_last_thrown_error(&result).await?; } else { self .language_server .commit_text(&evaluate_response.ts_code) .await; - self.set_last_eval_result(evaluate_result).await?; + self.set_last_eval_result(&result).await?; } - let value = self.get_eval_value(evaluate_result).await?; - Ok(match evaluate_exception_details { + let value = self.get_eval_value(&result).await?; + Ok(match exception_details { Some(_) => EvaluationOutput::Error(format!("Uncaught {}", value)), None => EvaluationOutput::Value(value), }) @@ -224,7 +227,7 @@ impl ReplSession { .as_ref() .unwrap() .value - .get("exceptionDetails") + .exception_details .is_some()) { self.evaluate_ts_expression(line).await @@ -235,66 +238,90 @@ impl ReplSession { async fn set_last_thrown_error( &mut self, - error: &Value, + error: &cdp::RemoteObject, ) -> Result<(), AnyError> { self.post_message_with_event_loop( "Runtime.callFunctionOn", - Some(json!({ - "executionContextId": self.context_id, - "functionDeclaration": "function (object) { Deno[Deno.internal].lastThrownError = object; }", - "arguments": [ - error, - ], - })), + Some(cdp::CallFunctionOnArgs { + function_declaration: "function (object) { Deno[Deno.internal].lastThrownError = object; }".to_string(), + object_id: None, + arguments: Some(vec![error.into()]), + silent: None, + return_by_value: None, + generate_preview: None, + user_gesture: None, + await_promise: None, + execution_context_id: Some(self.context_id), + object_group: None, + throw_on_side_effect: None + }), ).await?; Ok(()) } async fn set_last_eval_result( &mut self, - evaluate_result: &Value, + evaluate_result: &cdp::RemoteObject, ) -> Result<(), AnyError> { - self.post_message_with_event_loop( - "Runtime.callFunctionOn", - Some(json!({ - "executionContextId": self.context_id, - "functionDeclaration": "function (object) { Deno[Deno.internal].lastEvalResult = object; }", - "arguments": [ - evaluate_result, - ], - })), - ).await?; + self + .post_message_with_event_loop( + "Runtime.callFunctionOn", + Some(cdp::CallFunctionOnArgs { + function_declaration: + "function (object) { Deno[Deno.internal].lastEvalResult = object; }" + .to_string(), + object_id: None, + arguments: Some(vec![evaluate_result.into()]), + silent: None, + return_by_value: None, + generate_preview: None, + user_gesture: None, + await_promise: None, + execution_context_id: Some(self.context_id), + object_group: None, + throw_on_side_effect: None, + }), + ) + .await?; Ok(()) } pub async fn get_eval_value( &mut self, - evaluate_result: &Value, + evaluate_result: &cdp::RemoteObject, ) -> Result { // TODO(caspervonb) we should investigate using previews here but to keep things // consistent with the previous implementation we just get the preview result from // Deno.inspectArgs. let inspect_response = self.post_message_with_event_loop( "Runtime.callFunctionOn", - Some(json!({ - "executionContextId": self.context_id, - "functionDeclaration": r#"function (object) { + Some(cdp::CallFunctionOnArgs { + function_declaration: r#"function (object) { try { return Deno[Deno.internal].inspectArgs(["%o", object], { colors: !Deno.noColor }); } catch (err) { return Deno[Deno.internal].inspectArgs(["%o", err]); } - }"#, - "arguments": [ - evaluate_result, - ], - })), + }"#.to_string(), + object_id: None, + arguments: Some(vec![evaluate_result.into()]), + silent: None, + return_by_value: None, + generate_preview: None, + user_gesture: None, + await_promise: None, + execution_context_id: Some(self.context_id), + object_group: None, + throw_on_side_effect: None + }), ).await?; - let inspect_result = inspect_response.get("result").unwrap(); - let value = inspect_result.get("value").unwrap().as_str().unwrap(); + let response: cdp::CallFunctionOnResponse = + serde_json::from_value(inspect_response)?; + let value = response.result.value.unwrap(); + let s = value.as_str().unwrap(); - Ok(value.to_string()) + Ok(s.to_string()) } async fn evaluate_ts_expression( @@ -344,16 +371,29 @@ impl ReplSession { async fn evaluate_expression( &mut self, expression: &str, - ) -> Result { + ) -> Result { self .post_message_with_event_loop( "Runtime.evaluate", - Some(json!({ - "expression": expression, - "contextId": self.context_id, - "replMode": true, - })), + Some(cdp::EvaluateArgs { + expression: expression.to_string(), + object_group: None, + include_command_line_api: None, + silent: None, + context_id: Some(self.context_id), + return_by_value: None, + generate_preview: None, + user_gesture: None, + await_promise: None, + throw_on_side_effect: None, + timeout: None, + disable_breaks: None, + repl_mode: Some(true), + allow_unsafe_eval_blocked_by_csp: None, + unique_context_id: None, + }), ) .await + .and_then(|res| serde_json::from_value(res).map_err(|e| e.into())) } } diff --git a/core/inspector.rs b/core/inspector.rs index 97a04407d3..b6c86fb79d 100644 --- a/core/inspector.rs +++ b/core/inspector.rs @@ -649,10 +649,10 @@ impl LocalInspectorSession { self.notification_queue.split_off(0) } - pub async fn post_message( + pub async fn post_message( &mut self, method: &str, - params: Option, + params: Option, ) -> Result { let id = self.next_message_id; self.next_message_id += 1; diff --git a/test_util/src/pty.rs b/test_util/src/pty.rs index a8298d7a57..9ca02f857e 100644 --- a/test_util/src/pty.rs +++ b/test_util/src/pty.rs @@ -36,7 +36,7 @@ pub fn create_pty( .unwrap() .wait() .unwrap(); - unreachable!(); + std::process::exit(0); } }