Deserialize `Theme` directly into the heap to avoid stack overflow

Antonio Scandurra and Julia Risley created

Co-Authored-By: Julia Risley <julia@zed.dev>

Change summary

Cargo.lock                         | 27 +++++++++++++++++++++++++++
Cargo.toml                         |  1 +
crates/auto_update/Cargo.toml      |  1 +
crates/cli/Cargo.toml              |  1 +
crates/client/Cargo.toml           |  3 ++-
crates/collab/Cargo.toml           |  1 +
crates/collab_ui/Cargo.toml        |  1 +
crates/db/Cargo.toml               |  1 +
crates/editor/Cargo.toml           |  1 +
crates/feedback/Cargo.toml         |  3 ++-
crates/fs/Cargo.toml               |  1 +
crates/gpui/Cargo.toml             |  1 +
crates/language/Cargo.toml         |  1 +
crates/live_kit_client/Cargo.toml  |  4 +++-
crates/live_kit_server/Cargo.toml  |  1 +
crates/lsp/Cargo.toml              |  1 +
crates/plugin/Cargo.toml           |  1 +
crates/plugin_macros/Cargo.toml    |  1 +
crates/plugin_runtime/Cargo.toml   |  1 +
crates/project/Cargo.toml          |  1 +
crates/rpc/Cargo.toml              |  1 +
crates/search/Cargo.toml           |  1 +
crates/settings/Cargo.toml         |  1 +
crates/terminal/Cargo.toml         |  1 +
crates/terminal_view/Cargo.toml    |  1 +
crates/theme/Cargo.toml            |  1 +
crates/theme/src/theme_registry.rs | 11 ++++++++---
crates/vim/Cargo.toml              |  1 +
crates/workspace/Cargo.toml        |  3 ++-
crates/zed/Cargo.toml              |  1 +
plugins/Cargo.lock                 |  3 +++
plugins/json_language/Cargo.toml   |  1 +
32 files changed, 72 insertions(+), 7 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -518,6 +518,7 @@ dependencies = [
  "menu",
  "project",
  "serde",
+ "serde_derive",
  "serde_json",
  "settings",
  "smol",
@@ -1097,6 +1098,7 @@ dependencies = [
  "ipc-channel",
  "plist",
  "serde",
+ "serde_derive",
 ]
 
 [[package]]
@@ -1119,6 +1121,7 @@ dependencies = [
  "rand 0.8.5",
  "rpc",
  "serde",
+ "serde_derive",
  "settings",
  "smol",
  "sum_tree",
@@ -1228,6 +1231,7 @@ dependencies = [
  "sea-orm",
  "sea-query",
  "serde",
+ "serde_derive",
  "serde_json",
  "settings",
  "sha-1 0.9.8",
@@ -1269,6 +1273,7 @@ dependencies = [
  "postage",
  "project",
  "serde",
+ "serde_derive",
  "settings",
  "theme",
  "util",
@@ -1739,6 +1744,7 @@ dependencies = [
  "log",
  "parking_lot 0.11.2",
  "serde",
+ "serde_derive",
  "smol",
  "sqlez",
  "sqlez_macros",
@@ -1947,6 +1953,7 @@ dependencies = [
  "rand 0.8.5",
  "rpc",
  "serde",
+ "serde_derive",
  "settings",
  "smallvec",
  "smol",
@@ -2100,6 +2107,7 @@ dependencies = [
  "project",
  "search",
  "serde",
+ "serde_derive",
  "settings",
  "sysinfo",
  "theme",
@@ -2296,6 +2304,7 @@ dependencies = [
  "regex",
  "rope",
  "serde",
+ "serde_derive",
  "serde_json",
  "smol",
  "tempfile",
@@ -2667,6 +2676,7 @@ dependencies = [
  "schemars",
  "seahash",
  "serde",
+ "serde_derive",
  "serde_json",
  "simplelog",
  "smallvec",
@@ -3276,6 +3286,7 @@ dependencies = [
  "regex",
  "rpc",
  "serde",
+ "serde_derive",
  "serde_json",
  "settings",
  "similar",
@@ -3471,6 +3482,7 @@ dependencies = [
  "parking_lot 0.11.2",
  "postage",
  "serde",
+ "serde_derive",
  "serde_json",
  "sha2 0.10.6",
  "simplelog",
@@ -3491,6 +3503,7 @@ dependencies = [
  "prost-types 0.8.0",
  "reqwest",
  "serde",
+ "serde_derive",
  "sha2 0.10.6",
 ]
 
@@ -3531,6 +3544,7 @@ dependencies = [
  "parking_lot 0.11.2",
  "postage",
  "serde",
+ "serde_derive",
  "serde_json",
  "smol",
  "unindent",
@@ -4450,6 +4464,7 @@ dependencies = [
  "bincode",
  "plugin_macros",
  "serde",
+ "serde_derive",
 ]
 
 [[package]]
@@ -4460,6 +4475,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "serde",
+ "serde_derive",
  "syn",
 ]
 
@@ -4471,6 +4487,7 @@ dependencies = [
  "bincode",
  "pollster",
  "serde",
+ "serde_derive",
  "serde_json",
  "smol",
  "wasi-common",
@@ -4628,6 +4645,7 @@ dependencies = [
  "regex",
  "rpc",
  "serde",
+ "serde_derive",
  "serde_json",
  "settings",
  "sha2 0.10.6",
@@ -5250,6 +5268,7 @@ dependencies = [
  "rand 0.8.5",
  "rsa",
  "serde",
+ "serde_derive",
  "smol",
  "smol-timeout",
  "tempdir",
@@ -5673,6 +5692,7 @@ dependencies = [
  "postage",
  "project",
  "serde",
+ "serde_derive",
  "serde_json",
  "settings",
  "smallvec",
@@ -5861,6 +5881,7 @@ dependencies = [
  "postage",
  "schemars",
  "serde",
+ "serde_derive",
  "serde_json",
  "serde_path_to_error",
  "sqlez",
@@ -6481,6 +6502,7 @@ dependencies = [
  "procinfo",
  "rand 0.8.5",
  "serde",
+ "serde_derive",
  "settings",
  "shellexpand",
  "smallvec",
@@ -6512,6 +6534,7 @@ dependencies = [
  "project",
  "rand 0.8.5",
  "serde",
+ "serde_derive",
  "settings",
  "shellexpand",
  "smallvec",
@@ -6571,6 +6594,7 @@ dependencies = [
  "indexmap",
  "parking_lot 0.11.2",
  "serde",
+ "serde_derive",
  "serde_json",
  "serde_path_to_error",
  "toml",
@@ -7564,6 +7588,7 @@ dependencies = [
  "project",
  "search",
  "serde",
+ "serde_derive",
  "serde_json",
  "settings",
  "tokio",
@@ -8347,6 +8372,7 @@ dependencies = [
  "postage",
  "project",
  "serde",
+ "serde_derive",
  "serde_json",
  "settings",
  "smallvec",
@@ -8473,6 +8499,7 @@ dependencies = [
  "rust-embed",
  "search",
  "serde",
+ "serde_derive",
  "serde_json",
  "serde_path_to_error",
  "settings",

Cargo.toml 🔗

@@ -68,6 +68,7 @@ resolver = "2"
 
 [workspace.dependencies]
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] }
 rand = { version = "0.8" }
 

crates/auto_update/Cargo.toml 🔗

@@ -23,6 +23,7 @@ isahc = "1.7"
 lazy_static = "1.4"
 log = "0.4"
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 serde_json = { version = "1.0", features = ["preserve_order"] }
 smol = "1.2.5"
 tempdir = "0.3.7"

crates/cli/Cargo.toml 🔗

@@ -18,6 +18,7 @@ clap = { version = "3.1", features = ["derive"] }
 dirs = "3.0"
 ipc-channel = "0.16"
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-foundation = "0.9"

crates/client/Cargo.toml 🔗

@@ -35,7 +35,8 @@ time = { version = "0.3", features = ["serde", "serde-well-known"] }
 tiny_http = "0.8"
 uuid = { version = "1.1.2", features = ["v4"] }
 url = "2.2"
-serde = { version = "*", features = ["derive"] }
+serde = { version = "*", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 settings = { path = "../settings" }
 tempfile = "3"
 

crates/collab/Cargo.toml 🔗

@@ -42,6 +42,7 @@ scrypt = "0.7"
 sea-orm = { git = "https://github.com/zed-industries/sea-orm", rev = "18f4c691085712ad014a51792af75a9044bacee6", features = ["sqlx-postgres", "postgres-array", "runtime-tokio-rustls"] }
 sea-query = "0.27"
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 serde_json = "1.0"
 sha-1 = "0.9"
 sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "postgres", "json", "time", "uuid", "any"] }

crates/collab_ui/Cargo.toml 🔗

@@ -44,6 +44,7 @@ futures = "0.3"
 log = "0.4"
 postage = { version = "0.4.1", features = ["futures-traits"] }
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 
 [dev-dependencies]
 call = { path = "../call", features = ["test-support"] }

crates/db/Cargo.toml 🔗

@@ -24,6 +24,7 @@ lazy_static = "1.4.0"
 log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 parking_lot = "0.11.1"
 serde = { version = "1.0", features = ["derive"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 smol = "1.2"
 
 [dev-dependencies]

crates/editor/Cargo.toml 🔗

@@ -54,6 +54,7 @@ parking_lot = "0.11"
 postage = { version = "0.4", features = ["futures-traits"] }
 rand = { version = "0.8.3", optional = true }
 serde = { workspace = true }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 smallvec = { version = "1.6", features = ["union"] }
 smol = "1.2"
 tree-sitter-rust = { version = "*", optional = true }

crates/feedback/Cargo.toml 🔗

@@ -25,10 +25,11 @@ postage = { version = "0.4", features = ["futures-traits"] }
 project = { path = "../project" }
 search = { path = "../search" }
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 settings = { path = "../settings" }
 sysinfo = "0.27.1"
 theme = { path = "../theme" }
 tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" }
 urlencoding = "2.1.2"
 util = { path = "../util" }
-workspace = { path = "../workspace" }
+workspace = { path = "../workspace" }

crates/fs/Cargo.toml 🔗

@@ -24,6 +24,7 @@ smol = "1.2.5"
 regex = "1.5"
 git2 = { version = "0.15", default-features = false }
 serde = { workspace = true }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 serde_json = { workspace = true }
 log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 libc = "0.2"

crates/gpui/Cargo.toml 🔗

@@ -42,6 +42,7 @@ resvg = "0.14"
 schemars = "0.8"
 seahash = "4.1"
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 serde_json = "1.0"
 smallvec = { version = "1.6", features = ["union"] }
 smol = "1.2"

crates/language/Cargo.toml 🔗

@@ -47,6 +47,7 @@ postage = { version = "0.4.1", features = ["futures-traits"] }
 rand = { version = "0.8.3", optional = true }
 regex = "1.5"
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 serde_json = { version = "1", features = ["preserve_order"] }
 similar = "1.3"
 smallvec = { version = "1.6", features = ["union"] }

crates/live_kit_client/Cargo.toml 🔗

@@ -14,7 +14,7 @@ name = "test_app"
 
 [features]
 test-support = [
-    "async-trait", 
+    "async-trait",
     "collections/test-support",
     "gpui/test-support",
     "lazy_static",
@@ -63,9 +63,11 @@ lazy_static = "1.4"
 objc = "0.2"
 parking_lot = "0.11.1"
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 sha2 = "0.10"
 simplelog = "0.9"
 
 [build-dependencies]
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 serde_json = { version = "1.0", features = ["preserve_order"] }

crates/live_kit_server/Cargo.toml 🔗

@@ -20,6 +20,7 @@ prost = "0.8"
 prost-types = "0.8"
 reqwest = "0.11"
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 sha2 = "0.10"
 
 [build-dependencies]

crates/lsp/Cargo.toml 🔗

@@ -23,6 +23,7 @@ lsp-types = "0.91"
 parking_lot = "0.11"
 postage = { version = "0.4.1", features = ["futures-traits"] }
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 serde_json = { version = "1.0", features = ["raw_value"] }
 smol = "1.2"
 

crates/plugin/Cargo.toml 🔗

@@ -6,5 +6,6 @@ publish = false
 
 [dependencies]
 serde = "1.0"
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 bincode = "1.3"
 plugin_macros = { path = "../plugin_macros" }

crates/plugin_macros/Cargo.toml 🔗

@@ -12,4 +12,5 @@ syn = { version = "1.0", features = ["full",  "extra-traits"] }
 quote = "1.0"
 proc-macro2 = "1.0"
 serde = "1.0"
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 bincode = "1.3"

crates/plugin_runtime/Cargo.toml 🔗

@@ -10,6 +10,7 @@ wasmtime-wasi = "0.38"
 wasi-common = "0.38"
 anyhow = { version = "1.0", features = ["std"] }
 serde = "1.0"
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 serde_json = "1.0"
 bincode = "1.3"
 pollster = "0.2.5"

crates/project/Cargo.toml 🔗

@@ -49,6 +49,7 @@ pulldown-cmark = { version = "0.9.1", default-features = false }
 rand = "0.8.3"
 regex = "1.5"
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 serde_json = { version = "1.0", features = ["preserve_order"] }
 sha2 = "0.10"
 similar = "1.3"

crates/rpc/Cargo.toml 🔗

@@ -27,6 +27,7 @@ prost = "0.8"
 rand = "0.8"
 rsa = "0.4"
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 smol-timeout = "0.6"
 tracing = { version = "0.1.34", features = ["log"] }
 zstd = "0.11"

crates/search/Cargo.toml 🔗

@@ -24,6 +24,7 @@ futures = "0.3"
 log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 postage = { version = "0.4.1", features = ["futures-traits"] }
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 smallvec = { version = "1.6", features = ["union"] }
 smol = "1.2"
 

crates/settings/Cargo.toml 🔗

@@ -25,6 +25,7 @@ json_comments = "0.2"
 postage = { version = "0.4.1", features = ["futures-traits"] }
 schemars = "0.8"
 serde = { workspace = true }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 serde_json = { workspace = true }
 serde_path_to_error = "0.1.4"
 toml = "0.5"

crates/terminal/Cargo.toml 🔗

@@ -30,6 +30,7 @@ anyhow = "1"
 thiserror = "1.0"
 lazy_static = "1.4.0"
 serde = { version = "1.0", features = ["derive"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 
 [dev-dependencies]
 rand = "0.8.5"

crates/terminal_view/Cargo.toml 🔗

@@ -34,6 +34,7 @@ anyhow = "1"
 thiserror = "1.0"
 lazy_static = "1.4.0"
 serde = { version = "1.0", features = ["derive"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 
 
 

crates/theme/Cargo.toml 🔗

@@ -14,6 +14,7 @@ anyhow = "1.0.38"
 indexmap = "1.6.2"
 parking_lot = "0.11.1"
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 serde_json = { version = "1.0", features = ["preserve_order"] }
 serde_path_to_error = "0.1.4"
 toml = "0.5"

crates/theme/src/theme_registry.rs 🔗

@@ -2,6 +2,7 @@ use crate::{Theme, ThemeMeta};
 use anyhow::{Context, Result};
 use gpui::{fonts, AssetSource, FontCache};
 use parking_lot::Mutex;
+use serde::Deserialize;
 use serde_json::Value;
 use std::{collections::HashMap, sync::Arc};
 
@@ -56,12 +57,16 @@ impl ThemeRegistry {
             .with_context(|| format!("failed to load theme file {}", asset_path))?;
 
         // Allocate into the heap directly, the Theme struct is too large to fit in the stack.
-        let mut theme: Arc<Theme> = fonts::with_font_cache(self.font_cache.clone(), || {
-            serde_path_to_error::deserialize(&mut serde_json::Deserializer::from_slice(&theme_json))
+        let mut theme = fonts::with_font_cache(self.font_cache.clone(), || {
+            let mut theme = Box::new(Theme::default());
+            let mut deserializer = serde_json::Deserializer::from_slice(&theme_json);
+            let result = Theme::deserialize_in_place(&mut deserializer, &mut theme);
+            result.map(|_| theme)
         })?;
 
         // Reset name to be the file path, so that we can use it to access the stored themes
-        Arc::get_mut(&mut theme).unwrap().meta.name = name.into();
+        theme.meta.name = name.into();
+        let theme: Arc<Theme> = theme.into();
         self.themes.lock().insert(name.to_string(), theme.clone());
         Ok(theme)
     }

crates/vim/Cargo.toml 🔗

@@ -13,6 +13,7 @@ neovim = ["nvim-rs", "async-compat", "async-trait", "tokio"]
 
 [dependencies]
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 itertools = "0.10"
 log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 

crates/workspace/Cargo.toml 🔗

@@ -45,6 +45,7 @@ log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 parking_lot = "0.11.1"
 postage = { version = "0.4.1", features = ["futures-traits"] }
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 serde_json = { version = "1.0", features = ["preserve_order"] }
 smallvec = { version = "1.6", features = ["union"] }
 indoc = "1.0.4"
@@ -57,4 +58,4 @@ gpui = { path = "../gpui", features = ["test-support"] }
 project = { path = "../project", features = ["test-support"] }
 settings = { path = "../settings", features = ["test-support"] }
 fs = { path = "../fs", features = ["test-support"] }
-db = { path = "../db", features = ["test-support"] }
+db = { path = "../db", features = ["test-support"] }

crates/zed/Cargo.toml 🔗

@@ -88,6 +88,7 @@ regex = "1.5"
 rsa = "0.4"
 rust-embed = { version = "6.3", features = ["include-exclude"] }
 serde = { version = "1.0", features = ["derive", "rc"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 serde_json = { version = "1.0", features = ["preserve_order"] }
 serde_path_to_error = "0.1.4"
 simplelog = "0.9"

plugins/Cargo.lock 🔗

@@ -23,6 +23,7 @@ version = "0.1.0"
 dependencies = [
  "plugin",
  "serde",
+ "serde_derive",
  "serde_json",
 ]
 
@@ -33,6 +34,7 @@ dependencies = [
  "bincode",
  "plugin_macros",
  "serde",
+ "serde_derive",
 ]
 
 [[package]]
@@ -43,6 +45,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "serde",
+ "serde_derive",
  "syn",
 ]
 

plugins/json_language/Cargo.toml 🔗

@@ -6,6 +6,7 @@ edition = "2021"
 [dependencies]
 plugin = { path = "../../crates/plugin" }
 serde = { version = "1.0", features = ["derive"] }
+serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
 serde_json = "1.0"
 
 [lib]