summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/tests/unit/files_test.ts16
-rw-r--r--runtime/ops/fs.rs16
2 files changed, 30 insertions, 2 deletions
diff --git a/cli/tests/unit/files_test.ts b/cli/tests/unit/files_test.ts
index a509672c0..d15f1f538 100644
--- a/cli/tests/unit/files_test.ts
+++ b/cli/tests/unit/files_test.ts
@@ -791,3 +791,19 @@ Deno.test(
assertEquals(res, "hello world!");
},
);
+
+Deno.test(
+ { permissions: { read: true, write: true } },
+ async function readTextFileNonUtf8() {
+ const path = await Deno.makeTempFile();
+ const file = await Deno.open(path, { write: true });
+ await file.write(new TextEncoder().encode("hello "));
+ await file.write(new Uint8Array([0xC0]));
+ file.close();
+
+ const res = await Deno.readTextFile(path);
+ const resSync = Deno.readTextFileSync(path);
+ assertEquals(res, resSync);
+ assertEquals(res, "hello \uFFFD");
+ },
+);
diff --git a/runtime/ops/fs.rs b/runtime/ops/fs.rs
index 501c2ac5f..99b37916d 100644
--- a/runtime/ops/fs.rs
+++ b/runtime/ops/fs.rs
@@ -21,6 +21,7 @@ use deno_crypto::rand::Rng;
use log::debug;
use serde::Deserialize;
use serde::Serialize;
+use std::borrow::Cow;
use std::cell::RefCell;
use std::convert::From;
use std::env::{current_dir, set_current_dir, temp_dir};
@@ -2032,7 +2033,7 @@ fn op_readfile_text_sync(
let permissions = state.borrow_mut::<Permissions>();
let path = Path::new(&path);
permissions.read.check(path)?;
- Ok(std::fs::read_to_string(path)?)
+ Ok(string_from_utf8_lossy(std::fs::read(path)?))
}
#[op]
@@ -2075,7 +2076,7 @@ async fn op_readfile_text_async(
}
let fut = tokio::task::spawn_blocking(move || {
let path = Path::new(&path);
- Ok(String::from_utf8(std::fs::read(path)?)?)
+ Ok(string_from_utf8_lossy(std::fs::read(path)?))
});
if let Some(cancel_rid) = cancel_rid {
let cancel_handle = state
@@ -2088,3 +2089,14 @@ async fn op_readfile_text_async(
}
fut.await?
}
+
+// Like String::from_utf8_lossy but operates on owned values
+fn string_from_utf8_lossy(buf: Vec<u8>) -> String {
+ match String::from_utf8_lossy(&buf) {
+ // buf contained non-utf8 chars than have been patched
+ Cow::Owned(s) => s,
+ // SAFETY: if Borrowed then the buf only contains utf8 chars,
+ // we do this instead of .into_owned() to avoid copying the input buf
+ Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(buf) },
+ }
+}