Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: reduce memory leaks by upgrading to deno_core 0.299 #83

Merged
merged 1 commit into from
Jul 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
897 changes: 542 additions & 355 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ members = [
]

[workspace.dependencies]
deno_core = "0.243.0"
once_cell = "1.18"
deno_core = "0.299.0"
deno_console = "0.163.0"
deno_url = "0.163.0"
deno_webidl = "0.163.0"
dprint-core = "0.65.0"
serde = { version = "1.0.149", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
1 change: 1 addition & 0 deletions base/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ build = []
async-channel = "1.9.0"
deno_core = { workspace = true }
dprint-core = { workspace = true, features = ["process"] }
once_cell.workspace = true
serde = { workspace = true }
serde_json = { workspace = true }
sysinfo = { version = "0.29.10", default-features = false }
Expand Down
50 changes: 31 additions & 19 deletions base/src/build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::path::PathBuf;

use deno_core::snapshot_util::CreateSnapshotOutput;
use deno_core::Extension;
use deno_core::JsRuntimeForSnapshot;

Expand All @@ -11,24 +10,37 @@ pub struct CreateSnapshotOptions {
pub snapshot_path: PathBuf,
pub extensions: Vec<Extension>,
pub with_runtime_cb: Option<Box<WithRuntimeCb>>,
pub warmup_script: Option<&'static str>,
}

pub fn create_snapshot(options: CreateSnapshotOptions) -> CreateSnapshotOutput {
deno_core::snapshot_util::create_snapshot(deno_core::snapshot_util::CreateSnapshotOptions {
cargo_manifest_dir: options.cargo_manifest_dir,
snapshot_path: options.snapshot_path,
startup_snapshot: None,
extensions: options.extensions,
skip_op_registration: false,
#[cfg(debug_assertions)]
compression_cb: None,
#[cfg(not(debug_assertions))]
compression_cb: Some(Box::new(|vec, snapshot_slice| {
eprintln!("Compressing snapshot...");
vec.extend_from_slice(
&zstd::bulk::compress(snapshot_slice, 22).expect("snapshot compression failed"),
);
})),
with_runtime_cb: options.with_runtime_cb,
})
/// Creates a snapshot, returning the uncompressed bytes.
pub fn create_snapshot(options: CreateSnapshotOptions) -> Box<[u8]> {
let snapshot_output = deno_core::snapshot::create_snapshot(
deno_core::snapshot::CreateSnapshotOptions {
cargo_manifest_dir: options.cargo_manifest_dir,
startup_snapshot: None,
extensions: options.extensions,
skip_op_registration: false,
with_runtime_cb: options.with_runtime_cb,
extension_transpiler: None,
},
options.warmup_script,
)
.unwrap();

let mut output = snapshot_output.output.clone();
if !cfg!(debug_assertions) {
eprintln!("Compressing snapshot...");
let mut vec = Vec::with_capacity(output.len());
vec.extend((output.len() as u32).to_le_bytes());
vec.extend_from_slice(&zstd::bulk::compress(&output, 22).expect("snapshot compression failed"));
output = vec.into();
}
std::fs::write(&options.snapshot_path, output).unwrap();

for file in snapshot_output.files_loaded_during_snapshot {
println!("cargo:rerun-if-changed={}", file.display());
}

snapshot_output.output
}
32 changes: 15 additions & 17 deletions base/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,20 @@ use deno_core::anyhow::Error;
use deno_core::anyhow::Result;
use deno_core::serde_v8;
use deno_core::v8;
use deno_core::v8::Platform;
use deno_core::v8::SharedRef;
use deno_core::Extension;
use deno_core::PollEventLoopOptions;
use deno_core::RuntimeOptions;
use once_cell::sync::Lazy;
use serde::Deserialize;

use super::snapshot::Snapshot;

pub enum StartupSnapshot {
Static(&'static Snapshot),
Boxed(Snapshot),
}
static PLATFORM: Lazy<SharedRef<Platform>> =
Lazy::new(|| v8::new_default_platform(1, false).make_shared());

pub struct CreateRuntimeOptions {
pub extensions: Vec<Extension>,
pub startup_snapshot: Option<StartupSnapshot>,
pub startup_snapshot: Option<&'static [u8]>,
}

pub struct JsRuntime {
Expand All @@ -26,15 +25,10 @@ pub struct JsRuntime {

impl JsRuntime {
pub fn new(options: CreateRuntimeOptions) -> JsRuntime {
let maybe_snapshot = options.startup_snapshot.map(|s| match s {
StartupSnapshot::Static(s) => deno_core::Snapshot::Static(s.inner()),
StartupSnapshot::Boxed(s) => deno_core::Snapshot::Boxed(s.into_inner()),
});
let platform = v8::new_default_platform(1, false).make_shared();
JsRuntime {
inner: deno_core::JsRuntime::new(RuntimeOptions {
startup_snapshot: maybe_snapshot,
v8_platform: Some(platform),
startup_snapshot: options.startup_snapshot,
v8_platform: Some(PLATFORM.clone()),
extensions: options.extensions,
..Default::default()
}),
Expand All @@ -43,11 +37,11 @@ impl JsRuntime {

/// Call this once on the main thread.
pub fn initialize_main_thread() {
deno_core::JsRuntime::init_platform(None)
deno_core::JsRuntime::init_platform(Some(PLATFORM.clone()))
}

pub async fn execute_format_script(&mut self, code: String) -> Result<Option<String>, Error> {
let global = self.inner.execute_script("format.js", code.into())?;
let global = self.inner.execute_script("format.js", code)?;
let resolve = self.inner.resolve(global);
let global = self
.inner
Expand All @@ -66,6 +60,10 @@ impl JsRuntime {
}
}

pub fn execute_script(&mut self, script_name: &'static str, code: String) -> Result<(), Error> {
self.inner.execute_script(script_name, code).map(|_| ())
}

pub async fn execute_async_fn<'de, T>(
&mut self,
script_name: &'static str,
Expand All @@ -75,7 +73,7 @@ impl JsRuntime {
T: Deserialize<'de>,
{
let inner = &mut self.inner;
let fn_value = inner.execute_script(script_name, fn_name.into())?;
let fn_value = inner.execute_script(script_name, fn_name)?;
let fn_value = inner.resolve(fn_value).await?;
let mut scope = inner.handle_scope();
let fn_func: v8::Local<v8::Function> = v8::Local::new(&mut scope, fn_value).try_into()?;
Expand Down
42 changes: 12 additions & 30 deletions base/src/snapshot.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,14 @@
use std::path::Path;

pub struct Snapshot(Box<[u8]>);

impl Snapshot {
pub fn from_path(file_path: &Path) -> std::io::Result<Self> {
Self::deserialize(&std::fs::read(file_path)?)
}

pub fn deserialize(data: &[u8]) -> std::io::Result<Self> {
// compressing takes about a minute in debug, so only do it in release
if cfg!(debug_assertions) {
Ok(Self(data.to_vec().into_boxed_slice()))
} else {
Ok(Self(
zstd::bulk::decompress(
&data[4..],
u32::from_le_bytes(data[0..4].try_into().unwrap()) as usize,
)?
.into_boxed_slice(),
))
}
}

pub(crate) fn inner(&self) -> &[u8] {
&self.0
}

pub(crate) fn into_inner(self) -> Box<[u8]> {
self.0
pub fn deserialize_snapshot(data: &[u8]) -> std::io::Result<Box<[u8]>> {
// compressing takes about a minute in debug, so only do it in release
if cfg!(debug_assertions) {
Ok(data.to_vec().into_boxed_slice())
} else {
Ok(
zstd::bulk::decompress(
&data[4..],
u32::from_le_bytes(data[0..4].try_into().unwrap()) as usize,
)?
.into_boxed_slice(),
)
}
}
20 changes: 10 additions & 10 deletions plugin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ version = "0.46.1"
edition = "2021"

[dependencies]
deno_console = "0.130.0"
deno_core = { workspace = true }
deno_url = "0.130.0"
deno_webidl = "0.130.0"
deno_console.workspace = true
deno_core.workspace = true
deno_url.workspace = true
deno_webidl.workspace = true
dprint-core = { workspace = true, features = ["process"] }
dprint-plugin-deno-base = { version = "0.1.0", path = "../base" }
once_cell = "1.18"
serde = { workspace = true }
serde_json = { workspace = true }
once_cell.workspace = true
serde.workspace = true
serde_json.workspace = true

[build-dependencies]
deno_console = "0.130.0"
deno_console.workspace = true
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
dprint-plugin-deno-base = { version = "0.1.0", features = ["build"], path = "../base" }
deno_url = "0.130.0"
deno_webidl = "0.130.0"
deno_url.workspace = true
deno_webidl.workspace = true
sha256 = "1.4.0"
zstd = "0.13.0"

Expand Down
54 changes: 33 additions & 21 deletions plugin/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ use std::process::Command;
use deno_core::Extension;
use dprint_plugin_deno_base::runtime::CreateRuntimeOptions;
use dprint_plugin_deno_base::runtime::JsRuntime;
use dprint_plugin_deno_base::runtime::StartupSnapshot;
use dprint_plugin_deno_base::snapshot::Snapshot;
use dprint_plugin_deno_base::util::create_tokio_runtime;

fn main() {
Expand All @@ -19,7 +17,6 @@ fn main() {
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let startup_snapshot_path = out_dir.join("STARTUP_SNAPSHOT.bin");
let js_dir = root_dir.join("js");
let js_src_dir = js_dir.join("node").join("src");
let supported_extensions_path = out_dir.join("SUPPORTED_EXTENSIONS.json");

let build_result = Command::new("deno").args(["task", "build"]).status();
Expand All @@ -38,43 +35,53 @@ fn main() {
// ensure the build is invalidated if any of these files change
println!(
"cargo:rerun-if-changed={}",
js_src_dir.join("main.ts").display()
js_dir.join("node/main.ts").display()
);
println!(
"cargo:rerun-if-changed={}",
js_src_dir.join("package.json").display()
js_dir.join("node/package.json").display()
);
println!(
"cargo:rerun-if-changed={}",
js_src_dir.join("package-lock.json").display()
js_dir.join("node/package-lock.json").display()
);
println!(
"cargo:rerun-if-changed={}",
js_src_dir.join("shims/url.js").display()
js_dir.join("node/shims/url.js").display()
);

let startup_code_path = js_dir.join("node/dist/main.js");
if !startup_code_path.exists() {
panic!("Run `deno task build` first.");
}
create_snapshot(startup_snapshot_path.clone(), &startup_code_path);
let snapshot = create_snapshot(startup_snapshot_path.clone(), &startup_code_path);
let snapshot = Box::leak(snapshot);

// serialize the supported extensions
eprintln!("Creating runtime...");
let tokio_runtime = create_tokio_runtime();
let snapshot = Snapshot::from_path(&startup_snapshot_path).unwrap();
JsRuntime::initialize_main_thread();
let mut runtime = JsRuntime::new(CreateRuntimeOptions {
extensions: Vec::new(),
startup_snapshot: Some(StartupSnapshot::Boxed(snapshot)),
extensions: vec![
deno_webidl::deno_webidl::init_ops(),
deno_console::deno_console::init_ops(),
deno_url::deno_url::init_ops(),
main::init_ops(),
],
startup_snapshot: Some(snapshot),
});

eprintln!("Getting extensions...");
let file_extensions = tokio_runtime
.block_on(runtime.execute_async_fn::<Vec<String>>(
"deno:get_extensions.js",
"dprint.getExtensions".to_string(),
))
.unwrap();
let file_extensions: Vec<String> = tokio_runtime.block_on(async move {
let startup_text = get_startup_text(&startup_code_path);
runtime
.execute_script("dprint:prettier.js", startup_text.clone())
.unwrap();
runtime
.execute_async_fn::<Vec<String>>("deno:get_extensions.js", "dprint.getExtensions".to_string())
.await
.unwrap()
});
std::fs::write(
supported_extensions_path,
deno_core::serde_json::to_string(&file_extensions).unwrap(),
Expand All @@ -83,20 +90,25 @@ fn main() {
eprintln!("Done");
}

fn create_snapshot(snapshot_path: PathBuf, startup_code_path: &Path) {
let startup_text = std::fs::read_to_string(startup_code_path).unwrap();
fn create_snapshot(snapshot_path: PathBuf, startup_code_path: &Path) -> Box<[u8]> {
let startup_text = get_startup_text(startup_code_path);
dprint_plugin_deno_base::build::create_snapshot(
dprint_plugin_deno_base::build::CreateSnapshotOptions {
cargo_manifest_dir: env!("CARGO_MANIFEST_DIR"),
snapshot_path,
extensions: extensions(),
with_runtime_cb: Some(Box::new(move |runtime| {
runtime
.execute_script("dprint:prettier.js", startup_text.clone().into())
.execute_script("dprint:prettier.js", startup_text.clone())
.unwrap();
})),
warmup_script: None,
},
);
)
}

fn get_startup_text(startup_code_path: &Path) -> String {
std::fs::read_to_string(startup_code_path).unwrap()
}

deno_core::extension!(
Expand Down
15 changes: 9 additions & 6 deletions plugin/src/formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@ use dprint_core::plugins::FormatRequest;
use dprint_plugin_deno_base::channel::Formatter;
use dprint_plugin_deno_base::runtime::CreateRuntimeOptions;
use dprint_plugin_deno_base::runtime::JsRuntime;
use dprint_plugin_deno_base::runtime::StartupSnapshot;
use dprint_plugin_deno_base::snapshot::Snapshot;
use dprint_plugin_deno_base::snapshot::deserialize_snapshot;
use dprint_plugin_deno_base::util::set_v8_max_memory;
use once_cell::sync::Lazy;

use crate::config::PrettierConfig;

// Copied from Deno's codebase:
// https://github.com/denoland/deno/blob/daa7c6d32ab5a4029f8084e174d621f5562256be/cli/tsc.rs#L55
static STARTUP_SNAPSHOT: Lazy<Snapshot> = Lazy::new(
static STARTUP_SNAPSHOT: Lazy<Box<[u8]>> = Lazy::new(
#[cold]
#[inline(never)]
|| {
Expand All @@ -28,7 +27,7 @@ static STARTUP_SNAPSHOT: Lazy<Snapshot> = Lazy::new(
static COMPRESSED_COMPILER_SNAPSHOT: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/STARTUP_SNAPSHOT.bin"));

Snapshot::deserialize(COMPRESSED_COMPILER_SNAPSHOT).unwrap()
deserialize_snapshot(COMPRESSED_COMPILER_SNAPSHOT).unwrap()
},
);

Expand All @@ -39,8 +38,12 @@ pub struct PrettierFormatter {
impl Default for PrettierFormatter {
fn default() -> Self {
let runtime = JsRuntime::new(CreateRuntimeOptions {
extensions: Vec::new(),
startup_snapshot: Some(StartupSnapshot::Static(&STARTUP_SNAPSHOT)),
extensions: vec![
deno_webidl::deno_webidl::init_ops(),
deno_console::deno_console::init_ops(),
deno_url::deno_url::init_ops(),
],
startup_snapshot: Some(&STARTUP_SNAPSHOT),
});
Self { runtime }
}
Expand Down
Loading