difftreelog
perf reimplement AST codegen
in: master
10 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -275,6 +275,7 @@
"jrsonnet-gcmodule",
"rustc-hash",
"serde",
+ "structdump",
]
[[package]]
@@ -292,10 +293,10 @@
dependencies = [
"jrsonnet-gcmodule",
"jrsonnet-interner",
- "jrsonnet-stdlib",
"peg",
"serde",
"static_assertions",
+ "structdump",
]
[[package]]
@@ -312,6 +313,7 @@
"serde",
"serde_json",
"serde_yaml_with_quirks",
+ "structdump",
]
[[package]]
@@ -576,6 +578,28 @@
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
+name = "structdump"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0570327507bf281d8a6e6b0d4c082b12cb6bcee27efce755aa5efacd44076c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "structdump-derive",
+]
+
+[[package]]
+name = "structdump-derive"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29cc0b59cfa11f1bceda09a9a7e37e6a6c3138575fd24ade8aa9af6d09aedf28"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "syn"
version = "1.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
Cargo.tomldiffbeforeafterboth--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,6 +8,6 @@
opt-level = 3
lto = "fat"
codegen-units = 1
-debug = 0
+# debug = 0
panic = "abort"
-strip = true
+# strip = true
crates/jrsonnet-interner/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-interner/Cargo.toml
+++ b/crates/jrsonnet-interner/Cargo.toml
@@ -7,12 +7,19 @@
edition = "2021"
[features]
-default = ["serde"]
+default = []
+# Implement value serialization using structdump
+structdump = ["dep:structdump"]
+# Implement value serialization using serde
+#
+# Warning: serialized values won't be deduplicated
serde = ["dep:serde"]
[dependencies]
jrsonnet-gcmodule = { version = "0.3.4" }
serde = { version = "1.0", optional = true }
+structdump = { version = "0.2.0", optional = true }
+
rustc-hash = "1.1"
hashbrown = { version = "0.12.1", features = ["inline-more"] }
crates/jrsonnet-interner/src/lib.rsdiffbeforeafterboth1#![deny(2 unsafe_op_in_unsafe_fn,3 clippy::missing_safety_doc,4 clippy::undocumented_unsafe_blocks5)]6#![warn(clippy::pedantic, clippy::nursery)]7use std::{8 borrow::Cow,9 cell::RefCell,10 fmt::{self, Display},11 hash::{BuildHasherDefault, Hash, Hasher},12 ops::Deref,13 str,14};1516use hashbrown::HashMap;17use jrsonnet_gcmodule::Trace;18use rustc_hash::FxHasher;1920mod inner;21use inner::Inner;2223/// Interned string24///25/// Provides O(1) comparsions and hashing, cheap copy, and cheap conversion to [`IBytes`]26#[derive(Clone, PartialOrd, Ord, Eq)]27pub struct IStr(Inner);28impl Trace for IStr {29 fn is_type_tracked() -> bool {30 false31 }32}3334impl IStr {35 #[must_use]36 pub fn empty() -> Self {37 "".into()38 }39 #[must_use]40 pub fn as_str(&self) -> &str {41 self as &str42 }4344 #[must_use]45 pub fn cast_bytes(self) -> IBytes {46 IBytes(self.0.clone())47 }48}4950impl Deref for IStr {51 type Target = str;5253 fn deref(&self) -> &Self::Target {54 // SAFETY: Inner::check_utf8 is called on IStr construction, data is utf-855 unsafe { self.0.as_str_unchecked() }56 }57}5859impl PartialEq for IStr {60 fn eq(&self, other: &Self) -> bool {61 // all IStr should be inlined into same pool62 Inner::ptr_eq(&self.0, &other.0)63 }64}6566impl PartialEq<str> for IStr {67 fn eq(&self, other: &str) -> bool {68 self as &str == other69 }70}7172impl Hash for IStr {73 fn hash<H: Hasher>(&self, state: &mut H) {74 // IStr is always obtained from pool, where no string have duplicate, thus every unique string has unique address75 state.write_usize(Inner::as_ptr(&self.0).cast::<()>() as usize);76 }77}7879impl Drop for IStr {80 fn drop(&mut self) {81 #[cold]82 #[inline(never)]83 fn unpool(inner: &Inner) {84 // May fail on program termination85 let res = POOL.try_with(|pool| pool.borrow_mut().remove(inner));86 if res.is_ok() {87 debug_assert_eq!(Inner::strong_count(inner), 1);88 }89 }90 // First reference - current object, second - POOL91 if Inner::strong_count(&self.0) <= 2 {92 unpool(&self.0);93 }94 }95}9697impl fmt::Debug for IStr {98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {99 fmt::Debug::fmt(self as &str, f)100 }101}102103impl Display for IStr {104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {105 fmt::Display::fmt(self as &str, f)106 }107}108109/// Interned byte array110#[derive(Clone, PartialOrd, Ord, Eq)]111pub struct IBytes(Inner);112impl Trace for IBytes {113 fn is_type_tracked() -> bool {114 false115 }116}117118impl IBytes {119 #[must_use]120 pub fn cast_str(self) -> Option<IStr> {121 if Inner::check_utf8(&self.0) {122 Some(IStr(self.0.clone()))123 } else {124 None125 }126 }127 /// # Safety128 /// data should be valid utf8129 unsafe fn cast_str_unchecked(self) -> IStr {130 // SAFETY: data is utf8131 unsafe { Inner::assume_utf8(&self.0) };132 IStr(self.0.clone())133 }134135 #[must_use]136 pub const fn as_slice(&self) -> &[u8] {137 self.0.as_slice()138 }139}140141impl Deref for IBytes {142 type Target = [u8];143144 fn deref(&self) -> &Self::Target {145 self.0.as_slice()146 }147}148149impl PartialEq for IBytes {150 fn eq(&self, other: &Self) -> bool {151 // all IStr should be inlined into same pool152 Inner::ptr_eq(&self.0, &other.0)153 }154}155156impl Hash for IBytes {157 fn hash<H: Hasher>(&self, state: &mut H) {158 // IBytes is always obtained from pool, where no string have duplicate, thus every unique string has unique address159 state.write_usize(Inner::as_ptr(&self.0).cast::<()>() as usize);160 }161}162163impl Drop for IBytes {164 fn drop(&mut self) {165 #[cold]166 #[inline(never)]167 fn unpool(inner: &Inner) {168 // May fail on program termination169 let res = POOL.try_with(|pool| pool.borrow_mut().remove(inner));170 if res.is_ok() {171 debug_assert_eq!(Inner::strong_count(inner), 1);172 }173 }174 // First reference - current object, second - POOL175 if Inner::strong_count(&self.0) <= 2 {176 unpool(&self.0);177 }178 }179}180181impl fmt::Debug for IBytes {182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {183 fmt::Debug::fmt(self as &[u8], f)184 }185}186187impl<'c> From<Cow<'c, str>> for IStr {188 fn from(v: Cow<'c, str>) -> Self {189 intern_str(&v)190 }191}192impl From<&str> for IStr {193 fn from(v: &str) -> Self {194 intern_str(v)195 }196}197impl From<String> for IStr {198 fn from(s: String) -> Self {199 s.as_str().into()200 }201}202impl From<&[u8]> for IBytes {203 fn from(v: &[u8]) -> Self {204 intern_bytes(v)205 }206}207208impl serde::Serialize for IStr {209 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>210 where211 S: serde::Serializer,212 {213 self.as_str().serialize(serializer)214 }215}216217impl<'de> serde::Deserialize<'de> for IStr {218 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>219 where220 D: serde::Deserializer<'de>,221 {222 let str = <&str>::deserialize(deserializer)?;223 Ok(intern_str(str))224 }225}226227thread_local! {228 static POOL: RefCell<HashMap<Inner, (), BuildHasherDefault<FxHasher>>> = RefCell::new(HashMap::with_capacity_and_hasher(200, BuildHasherDefault::default()));229}230231#[must_use]232pub fn intern_bytes(bytes: &[u8]) -> IBytes {233 POOL.with(|pool| {234 let mut pool = pool.borrow_mut();235 let entry = pool.raw_entry_mut().from_key(bytes);236 match entry {237 hashbrown::hash_map::RawEntryMut::Occupied(mut i) => {238 IBytes(i.get_key_value().0.clone())239 }240 hashbrown::hash_map::RawEntryMut::Vacant(e) => {241 let (k, _) = e.insert(Inner::new_bytes(bytes), ());242 IBytes(k.clone())243 }244 }245 })246}247248#[must_use]249pub fn intern_str(str: &str) -> IStr {250 // SAFETY: Rust strings always utf8251 unsafe { intern_bytes(str.as_bytes()).cast_str_unchecked() }252}1#![deny(2 unsafe_op_in_unsafe_fn,3 clippy::missing_safety_doc,4 clippy::undocumented_unsafe_blocks5)]6#![warn(clippy::pedantic, clippy::nursery)]7use std::{8 borrow::Cow,9 cell::RefCell,10 fmt::{self, Display},11 hash::{BuildHasherDefault, Hash, Hasher},12 ops::Deref,13 str,14};1516use hashbrown::HashMap;17use jrsonnet_gcmodule::Trace;18use rustc_hash::FxHasher;1920mod inner;21use inner::Inner;2223/// Interned string24///25/// Provides O(1) comparsions and hashing, cheap copy, and cheap conversion to [`IBytes`]26#[derive(Clone, PartialOrd, Ord, Eq)]27pub struct IStr(Inner);28impl Trace for IStr {29 fn is_type_tracked() -> bool {30 false31 }32}3334impl IStr {35 #[must_use]36 pub fn empty() -> Self {37 "".into()38 }39 #[must_use]40 pub fn as_str(&self) -> &str {41 self as &str42 }4344 #[must_use]45 pub fn cast_bytes(self) -> IBytes {46 IBytes(self.0.clone())47 }48}4950impl Deref for IStr {51 type Target = str;5253 fn deref(&self) -> &Self::Target {54 // SAFETY: Inner::check_utf8 is called on IStr construction, data is utf-855 unsafe { self.0.as_str_unchecked() }56 }57}5859impl PartialEq for IStr {60 fn eq(&self, other: &Self) -> bool {61 // all IStr should be inlined into same pool62 Inner::ptr_eq(&self.0, &other.0)63 }64}6566impl PartialEq<str> for IStr {67 fn eq(&self, other: &str) -> bool {68 self as &str == other69 }70}7172impl Hash for IStr {73 fn hash<H: Hasher>(&self, state: &mut H) {74 // IStr is always obtained from pool, where no string have duplicate, thus every unique string has unique address75 state.write_usize(Inner::as_ptr(&self.0).cast::<()>() as usize);76 }77}7879impl Drop for IStr {80 fn drop(&mut self) {81 #[cold]82 #[inline(never)]83 fn unpool(inner: &Inner) {84 // May fail on program termination85 let res = POOL.try_with(|pool| pool.borrow_mut().remove(inner));86 if res.is_ok() {87 debug_assert_eq!(Inner::strong_count(inner), 1);88 }89 }90 // First reference - current object, second - POOL91 if Inner::strong_count(&self.0) <= 2 {92 unpool(&self.0);93 }94 }95}9697impl fmt::Debug for IStr {98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {99 fmt::Debug::fmt(self as &str, f)100 }101}102103impl Display for IStr {104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {105 fmt::Display::fmt(self as &str, f)106 }107}108109/// Interned byte array110#[derive(Clone, PartialOrd, Ord, Eq)]111pub struct IBytes(Inner);112impl Trace for IBytes {113 fn is_type_tracked() -> bool {114 false115 }116}117118impl IBytes {119 #[must_use]120 pub fn cast_str(self) -> Option<IStr> {121 if Inner::check_utf8(&self.0) {122 Some(IStr(self.0.clone()))123 } else {124 None125 }126 }127 /// # Safety128 /// data should be valid utf8129 unsafe fn cast_str_unchecked(self) -> IStr {130 // SAFETY: data is utf8131 unsafe { Inner::assume_utf8(&self.0) };132 IStr(self.0.clone())133 }134135 #[must_use]136 pub const fn as_slice(&self) -> &[u8] {137 self.0.as_slice()138 }139}140141impl Deref for IBytes {142 type Target = [u8];143144 fn deref(&self) -> &Self::Target {145 self.0.as_slice()146 }147}148149impl PartialEq for IBytes {150 fn eq(&self, other: &Self) -> bool {151 // all IStr should be inlined into same pool152 Inner::ptr_eq(&self.0, &other.0)153 }154}155156impl Hash for IBytes {157 fn hash<H: Hasher>(&self, state: &mut H) {158 // IBytes is always obtained from pool, where no string have duplicate, thus every unique string has unique address159 state.write_usize(Inner::as_ptr(&self.0).cast::<()>() as usize);160 }161}162163impl Drop for IBytes {164 fn drop(&mut self) {165 #[cold]166 #[inline(never)]167 fn unpool(inner: &Inner) {168 // May fail on program termination169 let res = POOL.try_with(|pool| pool.borrow_mut().remove(inner));170 if res.is_ok() {171 debug_assert_eq!(Inner::strong_count(inner), 1);172 }173 }174 // First reference - current object, second - POOL175 if Inner::strong_count(&self.0) <= 2 {176 unpool(&self.0);177 }178 }179}180181impl fmt::Debug for IBytes {182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {183 fmt::Debug::fmt(self as &[u8], f)184 }185}186187impl<'c> From<Cow<'c, str>> for IStr {188 fn from(v: Cow<'c, str>) -> Self {189 intern_str(&v)190 }191}192impl From<&str> for IStr {193 fn from(v: &str) -> Self {194 intern_str(v)195 }196}197impl From<String> for IStr {198 fn from(s: String) -> Self {199 s.as_str().into()200 }201}202impl From<&[u8]> for IBytes {203 fn from(v: &[u8]) -> Self {204 intern_bytes(v)205 }206}207208#[cfg(feature = "serde")]209impl serde::Serialize for IStr {210 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>211 where212 S: serde::Serializer,213 {214 self.as_str().serialize(serializer)215 }216}217218#[cfg(feature = "serde")]219impl<'de> serde::Deserialize<'de> for IStr {220 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>221 where222 D: serde::Deserializer<'de>,223 {224 let str = <&str>::deserialize(deserializer)?;225 Ok(intern_str(str))226 }227}228229#[cfg(feature = "structdump")]230impl structdump::Codegen for IStr {231 fn gen_code(232 &self,233 res: &mut structdump::CodegenResult,234 _unique: bool,235 ) -> structdump::TokenStream {236 let s: &str = self;237 res.add_code(238 structdump::quote! {239 structdump_import::IStr::from(#s)240 },241 Some(structdump::quote![structdump_import::IStr]),242 false,243 )244 }245}246247thread_local! {248 static POOL: RefCell<HashMap<Inner, (), BuildHasherDefault<FxHasher>>> = RefCell::new(HashMap::with_capacity_and_hasher(200, BuildHasherDefault::default()));249}250251#[must_use]252pub fn intern_bytes(bytes: &[u8]) -> IBytes {253 POOL.with(|pool| {254 let mut pool = pool.borrow_mut();255 let entry = pool.raw_entry_mut().from_key(bytes);256 match entry {257 hashbrown::hash_map::RawEntryMut::Occupied(mut i) => {258 IBytes(i.get_key_value().0.clone())259 }260 hashbrown::hash_map::RawEntryMut::Vacant(e) => {261 let (k, _) = e.insert(Inner::new_bytes(bytes), ());262 IBytes(k.clone())263 }264 }265 })266}267268#[must_use]269pub fn intern_str(str: &str) -> IStr {270 // SAFETY: Rust strings always utf8271 unsafe { intern_bytes(str.as_bytes()).cast_str_unchecked() }272}crates/jrsonnet-parser/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-parser/Cargo.toml
+++ b/crates/jrsonnet-parser/Cargo.toml
@@ -7,7 +7,23 @@
edition = "2021"
[features]
+default = []
exp-destruct = []
+# Implement serialization of AST using structdump
+#
+# Structdump generates code, which exactly replicated passed AST
+# Contrary to serde, has no code bloat problem, and is recommended
+#
+# The only limitation is serialized form is only useable if built from build script
+structdump = ["dep:structdump", "jrsonnet-interner/structdump"]
+# Implement serialization of AST using serde
+#
+# Warning: as serde doesn't deduplicate strings, `Source` struct will bloat
+# output binary with repeating source code. To resolve this issue, you should either
+# override serialization of this struct using custom `Serializer`/`Deserializer`,
+# not rely on Source, and fill its `source_code` with empty value, or use `structdump`
+# instead
+serde = ["dep:serde"]
[dependencies]
jrsonnet-interner = { path = "../jrsonnet-interner", version = "0.4.2" }
@@ -18,6 +34,4 @@
peg = "0.8.0"
serde = { version = "1.0", features = ["derive", "rc"], optional = true }
-
-[dev-dependencies]
-jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "0.4.2" }
+structdump = { version = "0.2.0", features = ["derive"], optional = true }
crates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -8,10 +8,13 @@
use jrsonnet_interner::IStr;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
+#[cfg(feature = "structdump")]
+use structdump::Codegen;
use crate::source::Source;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[derive(Debug, PartialEq, Trace)]
pub enum FieldName {
/// {fixed: 2}
@@ -20,6 +23,7 @@
Dyn(LocExpr),
}
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]
pub enum Visibility {
@@ -37,10 +41,12 @@
}
}
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Trace)]
pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub struct FieldMember {
@@ -51,6 +57,7 @@
pub value: LocExpr,
}
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub enum Member {
@@ -59,6 +66,7 @@
AssertStmt(AssertStmt),
}
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]
pub enum UnaryOpType {
@@ -84,6 +92,7 @@
}
}
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]
pub enum BinaryOpType {
@@ -150,11 +159,13 @@
}
/// name, default value
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub struct Param(pub Destruct, pub Option<LocExpr>);
/// Defined function parameters
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Trace)]
pub struct ParamsDesc(pub Rc<Vec<Param>>);
@@ -166,6 +177,7 @@
}
}
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub struct ArgsDesc {
@@ -187,6 +199,7 @@
Drop,
}
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Trace)]
pub enum Destruct {
@@ -216,6 +229,7 @@
}
}
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Trace)]
pub enum BindSpec {
@@ -230,14 +244,17 @@
},
}
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub struct IfSpecData(pub LocExpr);
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub struct ForSpecData(pub IStr, pub LocExpr);
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub enum CompSpec {
@@ -245,6 +262,7 @@
ForSpec(ForSpecData),
}
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub struct ObjComp {
@@ -256,6 +274,7 @@
pub compspecs: Vec<CompSpec>,
}
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub enum ObjBody {
@@ -263,6 +282,7 @@
ObjComp(ObjComp),
}
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, Clone, Copy, Trace)]
pub enum LiteralType {
@@ -274,6 +294,7 @@
False,
}
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub struct SliceDesc {
@@ -283,6 +304,7 @@
}
/// Syntax base
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub enum Expr {
@@ -351,6 +373,7 @@
}
/// file, begin offset, end offset
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, PartialEq, Eq, Trace)]
#[trace(skip)]
@@ -373,6 +396,7 @@
/// Holds AST expression and its location in source file
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[derive(Clone, PartialEq, Trace)]
pub struct LocExpr(pub Rc<Expr>, pub ExprLocation);
crates/jrsonnet-parser/src/source.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/source.rs
+++ b/crates/jrsonnet-parser/src/source.rs
@@ -9,9 +9,12 @@
use jrsonnet_interner::IStr;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
+#[cfg(feature = "structdump")]
+use structdump::Codegen;
use crate::location::{location_to_offset, offset_to_location, CodeLocation};
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq, Debug, Hash, Clone)]
pub enum SourcePath {
@@ -39,9 +42,10 @@
/// Either real file, or virtual
/// Hash of FileName always have same value as raw Path, to make it possible to use with raw_entry_mut
+#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, PartialEq, Eq, Debug)]
-pub struct Source(Rc<(SourcePath, IStr)>);
+pub struct Source(pub Rc<(SourcePath, IStr)>);
static_assertions::assert_eq_size!(Source, *const ());
impl Trace for Source {
crates/jrsonnet-stdlib/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-stdlib/Cargo.toml
+++ b/crates/jrsonnet-stdlib/Cargo.toml
@@ -7,9 +7,10 @@
edition = "2021"
[features]
-default = []
-# Serializes standard library AST, and deserialize on start, instead of parsing it every run from text
-serialized-stdlib = ["bincode", "jrsonnet-parser/serde"]
+default = ["codegenerated-stdlib"]
+# Speed-up initialization by generating code for parsed stdlib, instead
+# of invoking parser for it
+codegenerated-stdlib = ["jrsonnet-parser/structdump"]
# Enables legacy `std.thisFile` support, at the cost of worse caching
legacy-this-file = []
# Add order preservation flag to some functions
@@ -45,8 +46,5 @@
serde_yaml_with_quirks = "0.8.24"
[build-dependencies]
-jrsonnet-parser = { path = "../jrsonnet-parser", version = "0.4.2", features = [
- "serde",
-] }
-serde = "1.0"
-bincode = "1.3"
+jrsonnet-parser = { path = "../jrsonnet-parser", version = "0.4.2" }
+structdump = { version = "0.2.0", features = ["derive"] }
crates/jrsonnet-stdlib/build.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/build.rs
+++ b/crates/jrsonnet-stdlib/build.rs
@@ -1,7 +1,7 @@
use std::{borrow::Cow, env, fs::File, io::Write, path::Path};
-use bincode::serialize;
use jrsonnet_parser::{parse, ParserSettings, Source};
+use structdump::CodegenResult;
fn main() {
let parsed = parse(
@@ -15,10 +15,15 @@
)
.expect("parse");
+ let mut out = CodegenResult::default();
+
+ let v = out.codegen(&parsed, true);
+
{
let out_dir = env::var("OUT_DIR").unwrap();
- let dest_path = Path::new(&out_dir).join("stdlib.bincode");
+ let dest_path = Path::new(&out_dir).join("stdlib.rs");
let mut f = File::create(&dest_path).unwrap();
- f.write_all(&serialize(&parsed).unwrap()).unwrap();
+ f.write_all(v.to_string().replace(';', ";\n").as_bytes())
+ .unwrap();
}
}
crates/jrsonnet-stdlib/src/expr.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/expr.rs
+++ b/crates/jrsonnet-stdlib/src/expr.rs
@@ -1,22 +1,103 @@
-use std::borrow::Cow;
+use jrsonnet_parser::LocExpr;
-use jrsonnet_parser::{LocExpr, ParserSettings, Source};
+mod structdump_import {
+ pub(super) use std::{borrow::Cow, rc::Rc};
-pub const STDLIB_STR: &str = include_str!("./std.jsonnet");
+ pub(super) use jrsonnet_parser::*;
+ pub(super) use vec;
+ pub(super) use Option;
+}
pub fn stdlib_expr() -> LocExpr {
#[cfg(feature = "serialized-stdlib")]
{
+ use bincode::{BincodeRead, DefaultOptions, Options};
+ use serde::{Deserialize, Deserializer};
+
+ struct LocDeserializer<R, O: Options> {
+ source: Source,
+ wrapped: bincode::Deserializer<R, O>,
+ }
+ macro_rules! delegate {
+ ($(fn $name:ident($($arg:ident: $ty:ty),*))+) => {$(
+ fn $name<V>(mut self $(, $arg: $ty)*, visitor: V) -> Result<V::Value, Self::Error>
+ where V: serde::de::Visitor<'de>,
+ {
+ self.wrapped.$name($($arg,)* visitor)
+ }
+ )+};
+ }
+ impl<'de, R, O> Deserializer<'de> for LocDeserializer<R, O>
+ where
+ R: BincodeRead<'de>,
+ O: Options,
+ {
+ type Error = <&'de mut bincode::Deserializer<R, O> as Deserializer<'de>>::Error;
+
+ delegate! {
+ fn deserialize_any()
+ fn deserialize_bool()
+ fn deserialize_u16()
+ fn deserialize_u32()
+ fn deserialize_u64()
+ fn deserialize_i16()
+ fn deserialize_i32()
+ fn deserialize_i64()
+ fn deserialize_f32()
+ fn deserialize_f64()
+ fn deserialize_u128()
+ fn deserialize_i128()
+ fn deserialize_u8()
+ fn deserialize_i8()
+ fn deserialize_unit()
+ fn deserialize_char()
+ fn deserialize_str()
+ fn deserialize_string()
+ fn deserialize_bytes()
+ fn deserialize_byte_buf()
+ fn deserialize_enum(name: &'static str, variants: &'static [&'static str])
+ fn deserialize_tuple(len: usize)
+ fn deserialize_option()
+ fn deserialize_seq()
+ fn deserialize_map()
+ fn deserialize_struct(name: &'static str, fields: &'static [&'static str])
+ fn deserialize_identifier()
+ fn deserialize_newtype_struct(name: &'static str)
+ fn deserialize_unit_struct(name: &'static str)
+ fn deserialize_tuple_struct(name: &'static str, len: usize)
+ fn deserialize_ignored_any()
+ }
+
+ fn is_human_readable(&self) -> bool {
+ false
+ }
+ }
+
+ // In build.rs, Source object is populated with empty values, deserializer wrapper loads correct values on deserialize
+ let mut deserializer = bincode::Deserializer::from_slice(
+ include_bytes!(concat!(env!("OUT_DIR"), "/stdlib.bincode")),
+ DefaultOptions::new()
+ .with_fixint_encoding()
+ .allow_trailing_bytes(),
+ );
+
// Should not panic, stdlib.bincode is generated in build.rs
- return bincode::deserialize(include_bytes!(concat!(env!("OUT_DIR"), "/stdlib.bincode")))
- .unwrap();
+ LocExpr::deserialize(&mut deserializer).unwrap()
}
- jrsonnet_parser::parse(
- STDLIB_STR,
- &ParserSettings {
- file_name: Source::new_virtual(Cow::Borrowed("<std>"), STDLIB_STR.into()),
- },
- )
- .unwrap()
+ #[cfg(feature = "codegenerated-stdlib")]
+ {
+ include!(concat!(env!("OUT_DIR"), "/stdlib.rs"))
+ }
+
+ #[cfg(not(feature = "codegenerated-stdlib"))]
+ {
+ jrsonnet_parser::parse(
+ STDLIB_STR,
+ &ParserSettings {
+ file_name: Source::new_virtual(Cow::Borrowed("<std>"), STDLIB_STR.into()),
+ },
+ )
+ .unwrap()
+ }
}