Repository: denoland/deno
Pull request: fix(compile): temporarily fallback to reading resource data from file on windows

Temp hacky fix for https://github.com/denoland/deno/issues/28982

Changed files:
filename: cli/rt/binary.rs
patch: @@ -3,7 +3,11 @@
 use std::borrow::Cow;
 use std::collections::HashMap;
 use std::ffi::OsString;
+use std::fs::File;
+use std::io::BufRead;
+use std::io::BufReader;
 use std::io::ErrorKind;
+use std::io::Read;
 use std::path::Path;
 use std::path::PathBuf;
 use std::sync::Arc;
@@ -58,11 +62,7 @@ pub struct StandaloneData {
 pub fn extract_standalone(
   cli_args: Cow<Vec<OsString>>,
 ) -> Result<StandaloneData, AnyError> {
-  let Some(data) = libsui::find_section("d3n0l4nd")
-    .context("Failed reading standalone binary section.")?
-  else {
-    bail!("Could not find standalone binary section.")
-  };
+  let data = find_section()?;
 
   let root_path = {
     let maybe_current_exe = std::env::current_exe().ok();
@@ -117,6 +117,118 @@ pub fn extract_standalone(
   })
 }
 
+fn find_section() -> Result<&'static [u8], AnyError> {
+  if std::env::var_os("DENO_INTERNAL_RT_USE_FILE_FALLBACK").is_some() {
+    return read_from_file_fallback();
+  }
+  match libsui::find_section("d3n0l4nd")
+    .context("Failed reading standalone binary section.")
+  {
+    Ok(Some(data)) => Ok(data),
+    Ok(None) => bail!("Could not find standalone binary section."),
+    Err(err) => {
+      if cfg!(windows) {
+        if let Ok(data) = read_from_file_fallback() {
+          return Ok(data);
+        }
+      }
+
+      Err(err)
+    }
+  }
+}
+
+/// This is a temporary hacky fallback until we can find
+/// a fix for https://github.com/denoland/deno/issues/28982
+fn read_from_file_fallback() -> Result<&'static [u8], AnyError> {
+  // search for DENOLAND in utf16
+  const MARKER: &[u8] = &[
+    0x44, 0x00, 0x33, 0x00, 0x4E, 0x00, 0x30, 0x00, 0x4C, 0x00, 0x34, 0x00,
+    0x4E, 0x00, 0x44, 0x00,
+  ];
+  const ASCII_SENTINEL: &[u8] = b"d3n0l4nd";
+  let file_path = std::env::current_exe()?;
+  const BUF_CAP: usize = 64 * 1024;
+
+  let file = File::open(file_path)?;
+  let mut r = BufReader::with_capacity(BUF_CAP, file);
+
+  fn scan<R: Read>(
+    reader: &mut BufReader<R>,
+    needle: &[u8],
+  ) -> std::io::Result<Option<Vec<u8>>> {
+    let mut overlap = Vec::<u8>::with_capacity(needle.len().saturating_sub(1));
+    let mut out = None;
+
+    loop {
+      let chunk = reader.fill_buf()?;
+      if chunk.is_empty() {
+        return Ok(out);
+      }
+
+      // search in overlap+chunk
+      let mut search_space = overlap.clone();
+      search_space.extend_from_slice(chunk);
+
+      if let Some(pos) =
+        search_space.windows(needle.len()).position(|w| w == needle)
+      {
+        // How far into `chunk` does the match **end**?
+        let bytes_before_chunk = overlap.len();
+        let match_end_in_chunk = pos + needle.len() - bytes_before_chunk;
+
+        // Consume up to the end of the needle
+        reader.consume(match_end_in_chunk);
+
+        // Everything *after* the match that is already in memory
+        let tail_in_mem = &search_space[pos..]; // begins with the needle
+        out = Some(tail_in_mem.to_vec());
+        break;
+      }
+
+      // Prepare next overlap (needle.len()-1 bytes)
+      overlap.clear();
+      let keep = needle.len().saturating_sub(1);
+      if search_space.len() >= keep {
+        overlap.extend_from_slice(&search_space[search_space.len() - keep..]);
+      } else {
+        overlap.extend_from_slice(&search_space);
+      }
+
+      let chunk_len = chunk.len(); // prevent multiple borrow
+      reader.consume(chunk_len);
+    }
+
+    Ok(out)
+  }
+
+  // skip up to and including MARKER
+  if scan(&mut r, MARKER)?.is_none() {
+    bail!("Failed to find.");
+  }
+
+  // collect from ASCII_SENTINEL onward
+  let mut result = scan(&mut r, ASCII_SENTINEL)?.ok_or_else(|| {
+    std::io::Error::new(
+      std::io::ErrorKind::UnexpectedEof,
+      "ASCII sentinel not found",
+    )
+  })?;
+
+  r.read_to_end(&mut result)?;
+
+  if let Some(last_pos) = result
+    .windows(ASCII_SENTINEL.len())
+    .rposition(|w| w == ASCII_SENTINEL)
+  {
+    result.truncate(last_pos + ASCII_SENTINEL.len());
+  } else {
+    bail!("Failed to find.");
+  }
+
+  Ok(Box::leak(result.into_boxed_slice()))
+}
+
 pub struct DeserializedDataSection {
   pub metadata: Metadata,
   pub npm_snapshot: Option<ValidSerializedNpmResolutionSnapshot>,

filename: cli/standalone/binary.rs
patch: @@ -267,6 +267,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
     // Phase 2 of the 'min sized' deno compile RFC talks
     // about adding this as a flag.
     if let Some(path) = get_dev_binary_path() {
+      log::debug!("Resolved denort: {}", path.to_string_lossy());
       return std::fs::read(&path).with_context(|| {
         format!("Could not find denort at '{}'", path.to_string_lossy())
       });
@@ -286,6 +287,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
 
     let download_directory = self.deno_dir.dl_folder_path();
     let binary_path = download_directory.join(&binary_path_suffix);
+    log::debug!("Resolved denort: {}", binary_path.display());
 
     let read_file = |path: &Path| -> Result<Vec<u8>, AnyError> {
       std::fs::read(path).with_context(|| format!("Reading {}", path.display()))

filename: tests/specs/compile/fallback/__test__.jsonc
patch: @@ -0,0 +1,16 @@
+{
+  "envs": {
+    "DENO_INTERNAL_RT_USE_FILE_FALLBACK": "1"
+  },
+  "tempDir": true,
+  "steps": [{
+    "if": "windows",
+    "args": "compile --output bin main.ts",
+    "output": "[WILDCARD]"
+  }, {
+    "if": "windows",
+    "commandName": "./bin",
+    "args": [],
+    "output": "HI\n"
+  }]
+}

filename: tests/specs/compile/fallback/main.ts
patch: @@ -0,0 +1 @@
+console.log("HI");