Skip to content

Commit 4bd4cd3

Browse files
authored
fix(inspector): Preserve floating point precision from inspector response (#1141)
Fixes denoland/deno#29059 Messages from the inspector that hold a number value can result in the loss of floating point precision when deserialized. When a user sends `0.9999999999999999` in the REPL, V8 returns a [RemoteObject](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObject) that contains the field `"value": 0.9999999999999999`. However, when it's deserialized into `serde_json::Value` here: https://github.com/denoland/deno_core/blob/fc1ef37491950e82d2fb2e5e426a55723e730ce1/core/inspector.rs#L883-L884 `value` becomes `serde_json::Number(1.0)`, causing the REPL to print `1`. Enabling the `float_roundtrip` feature flag of serde json resolves this.
1 parent fc1ef37 commit 4bd4cd3

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ parking_lot.workspace = true
4444
percent-encoding.workspace = true
4545
pin-project.workspace = true
4646
serde.workspace = true
47-
serde_json = { workspace = true, features = ["preserve_order"] }
47+
serde_json = { workspace = true, features = ["float_roundtrip", "preserve_order"] }
4848
serde_v8.workspace = true
4949
smallvec.workspace = true
5050
sourcemap.workspace = true

core/runtime/tests/misc.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ use cooked_waker::WakeRef;
1111
use deno_error::JsErrorBox;
1212
use parking_lot::Mutex;
1313
use rstest::rstest;
14+
use serde_json::Value;
15+
use serde_json::json;
1416
use std::borrow::Cow;
1517
use std::collections::HashMap;
1618
use std::collections::HashSet;
1719
use std::future::poll_fn;
20+
use std::pin::pin;
1821
use std::rc::Rc;
1922
use std::sync::Arc;
2023
use std::sync::atomic::AtomicBool;
@@ -437,6 +440,66 @@ fn inspector() {
437440
runtime.execute_script("check.js", "null").unwrap();
438441
}
439442

443+
#[rstest]
444+
// https://github.com/denoland/deno/issues/29059
445+
#[case(0.9999999999999999)]
446+
#[case(31.245270191439438)]
447+
#[case(117.63331139400017)]
448+
#[tokio::test]
449+
async fn test_preserve_float_precision_from_local_inspector_evaluate(
450+
#[case] input: f64,
451+
) {
452+
let mut runtime = JsRuntime::new(RuntimeOptions {
453+
inspector: true,
454+
..Default::default()
455+
});
456+
457+
let result = local_inspector_evaluate(&mut runtime, &format!("{}", input))
458+
.await
459+
.unwrap();
460+
461+
assert_eq!(
462+
result["result"]["value"],
463+
Value::Number(serde_json::Number::from_f64(input).unwrap()),
464+
);
465+
}
466+
467+
async fn local_inspector_evaluate(
468+
runtime: &mut JsRuntime,
469+
expression: &str,
470+
) -> Result<Value, CoreError> {
471+
let session_options = inspector::InspectorSessionOptions {
472+
kind: inspector::InspectorSessionKind::NonBlocking {
473+
wait_for_disconnect: true,
474+
},
475+
};
476+
477+
let mut local_inspector_session = runtime
478+
.inspector()
479+
.borrow()
480+
.create_local_session(session_options);
481+
482+
let post_message_future = pin!(local_inspector_session.post_message(
483+
"Runtime.evaluate",
484+
Some(json!({
485+
"expression": expression,
486+
})),
487+
));
488+
489+
// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObject
490+
let remote_object_result = runtime
491+
.with_event_loop_future(
492+
post_message_future,
493+
PollEventLoopOptions {
494+
pump_v8_message_loop: false,
495+
..Default::default()
496+
},
497+
)
498+
.await?;
499+
500+
Ok(remote_object_result)
501+
}
502+
440503
#[test]
441504
fn test_get_module_namespace() {
442505
let mut runtime = JsRuntime::new(RuntimeOptions {

0 commit comments

Comments
 (0)