diff --git a/src/cached_source.rs b/src/cached_source.rs index 7b8ef2dc..876f4473 100644 --- a/src/cached_source.rs +++ b/src/cached_source.rs @@ -94,13 +94,43 @@ impl Source for CachedSource { self.source().len() } - fn map(&self, options: &MapOptions) -> Option { + fn map(&self, options: &MapOptions) -> Option> { if let Some(map) = self.cached_maps.get(options) { - map.clone() + match map.as_ref() { + Some(map) => { + #[allow(unsafe_code)] + // SAFETY: We guarantee that once a `SourceMap` is stored in the cache, it will never be removed. + // Therefore, even if we force its lifetime to be longer, the reference remains valid. + // This is based on the following assumptions: + // 1. `SourceMap` will be valid for the entire duration of the application. + // 2. The cached `SourceMap` will not be manually removed or replaced, ensuring the reference's safety. + let map = unsafe { + std::mem::transmute::<&SourceMap, &'static SourceMap>(map) + }; + Some(Cow::Borrowed(map)) + } + None => None, + } } else { let map = self.inner.map(options); - self.cached_maps.insert(options.clone(), map.clone()); - map + self + .cached_maps + .insert(options.clone(), map.map(|m| m.into_owned())); + match self.cached_maps.get(options).unwrap().as_ref() { + Some(map) => { + #[allow(unsafe_code)] + // SAFETY: We guarantee that once a `SourceMap` is stored in the cache, it will never be removed. + // Therefore, even if we force its lifetime to be longer, the reference remains valid. + // This is based on the following assumptions: + // 1. `SourceMap` will be valid for the entire duration of the application. + // 2. The cached `SourceMap` will not be manually removed or replaced, ensuring the reference's safety. + let map = unsafe { + std::mem::transmute::<&SourceMap, &'static SourceMap>(map) + }; + Some(Cow::Borrowed(map)) + } + None => None, + } } } @@ -254,7 +284,7 @@ mod tests { ); assert_eq!( *clone.cached_maps.get(&map_options).unwrap().value(), - source.map(&map_options) + source.map(&map_options).map(|map| map.into_owned()) ); } diff --git a/src/concat_source.rs b/src/concat_source.rs index 7435fcb6..230785ca 100644 --- a/src/concat_source.rs +++ b/src/concat_source.rs @@ -39,7 +39,7 @@ use crate::{ /// "Hello World\nconsole.log('test');\nconsole.log('test2');\nHello2\n" /// ); /// assert_eq!( -/// source.map(&MapOptions::new(false)).unwrap(), +/// source.map(&MapOptions::new(false)).unwrap().into_owned(), /// SourceMap::from_json( /// r#"{ /// "version": 3, @@ -127,8 +127,8 @@ impl Source for ConcatSource { self.children().iter().map(|child| child.size()).sum() } - fn map(&self, options: &MapOptions) -> Option { - get_map(self, options) + fn map(&self, options: &MapOptions) -> Option> { + get_map(self, options).map(Cow::Owned) } fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> { @@ -347,7 +347,7 @@ mod tests { assert_eq!(source.size(), 62); assert_eq!(source.source(), expected_source); assert_eq!( - source.map(&MapOptions::new(false)).unwrap(), + source.map(&MapOptions::new(false)).unwrap().into_owned(), SourceMap::from_json( r#"{ "version": 3, @@ -363,7 +363,7 @@ mod tests { .unwrap() ); assert_eq!( - source.map(&MapOptions::default()).unwrap(), + source.map(&MapOptions::default()).unwrap().into_owned(), SourceMap::from_json( r#"{ "version": 3, @@ -397,7 +397,7 @@ mod tests { assert_eq!(source.size(), 62); assert_eq!(source.source(), expected_source); assert_eq!( - source.map(&MapOptions::new(false)).unwrap(), + source.map(&MapOptions::new(false)).unwrap().into_owned(), SourceMap::from_json( r#"{ "version": 3, @@ -413,7 +413,7 @@ mod tests { .unwrap() ); assert_eq!( - source.map(&MapOptions::default()).unwrap(), + source.map(&MapOptions::default()).unwrap().into_owned(), SourceMap::from_json( r#"{ "version": 3, @@ -447,7 +447,7 @@ mod tests { assert_eq!(source.size(), 62); assert_eq!(source.source(), expected_source); assert_eq!( - source.map(&MapOptions::new(false)).unwrap(), + source.map(&MapOptions::new(false)).unwrap().into_owned(), SourceMap::from_json( r#"{ "version": 3, @@ -463,7 +463,7 @@ mod tests { .unwrap() ); assert_eq!( - source.map(&MapOptions::default()).unwrap(), + source.map(&MapOptions::default()).unwrap().into_owned(), SourceMap::from_json( r#"{ "version": 3, @@ -513,7 +513,7 @@ mod tests { assert_eq!(source.buffer(), expected_source.as_bytes()); let map = source.map(&MapOptions::new(false)).unwrap(); - assert_eq!(map, expected_map1); + assert_eq!(map.into_owned(), expected_map1); // TODO: test hash } @@ -549,7 +549,7 @@ mod tests { ]); assert_eq!( - source.map(&MapOptions::default()).unwrap(), + source.map(&MapOptions::default()).unwrap().into_owned(), SourceMap::from_json( r#"{ "mappings": "AAAA,K,CCAA,M;ADAA;;ACAA", diff --git a/src/original_source.rs b/src/original_source.rs index b73c275d..b0f57d92 100644 --- a/src/original_source.rs +++ b/src/original_source.rs @@ -63,8 +63,8 @@ impl Source for OriginalSource { self.value.len() } - fn map(&self, options: &MapOptions) -> Option { - get_map(self, options) + fn map(&self, options: &MapOptions) -> Option> { + get_map(self, options).map(Cow::Owned) } fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> { diff --git a/src/raw_source.rs b/src/raw_source.rs index 388ea69d..4db5205a 100644 --- a/src/raw_source.rs +++ b/src/raw_source.rs @@ -137,7 +137,7 @@ impl Source for RawSource { } } - fn map(&self, _: &MapOptions) -> Option { + fn map(&self, _: &MapOptions) -> Option> { None } @@ -274,7 +274,7 @@ impl Source for RawStringSource { self.0.len() } - fn map(&self, _: &MapOptions) -> Option { + fn map(&self, _: &MapOptions) -> Option> { None } @@ -373,7 +373,7 @@ impl Source for RawBufferSource { self.value.len() } - fn map(&self, _: &MapOptions) -> Option { + fn map(&self, _: &MapOptions) -> Option> { None } diff --git a/src/replace_source.rs b/src/replace_source.rs index 378cd841..7673ca17 100644 --- a/src/replace_source.rs +++ b/src/replace_source.rs @@ -216,13 +216,13 @@ impl Source for ReplaceSource { self.source().len() } - fn map(&self, options: &crate::MapOptions) -> Option { + fn map(&self, options: &crate::MapOptions) -> Option> { let replacements = self.replacements.lock().unwrap(); if replacements.is_empty() { return self.inner.map(options); } drop(replacements); - get_map(self, options) + get_map(self, options).map(Cow::Owned) } fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> { diff --git a/src/source.rs b/src/source.rs index 25adbb5c..a43e555b 100644 --- a/src/source.rs +++ b/src/source.rs @@ -39,7 +39,7 @@ pub trait Source: fn size(&self) -> usize; /// Get the [SourceMap]. - fn map(&self, options: &MapOptions) -> Option; + fn map(&self, options: &MapOptions) -> Option>; /// Update hash based on the source. fn update_hash(&self, state: &mut dyn Hasher) { @@ -63,7 +63,7 @@ impl Source for BoxSource { self.as_ref().size() } - fn map(&self, options: &MapOptions) -> Option { + fn map(&self, options: &MapOptions) -> Option> { self.as_ref().map(options) } @@ -370,8 +370,8 @@ impl SourceMap { } /// Generate source map to a json string. - pub fn to_json(self) -> Result { - let json = simd_json::serde::to_string(&self)?; + pub fn to_json(&self) -> Result { + let json = simd_json::serde::to_string(self)?; Ok(json) } diff --git a/src/source_map_source.rs b/src/source_map_source.rs index 07b32a05..5f2da52b 100644 --- a/src/source_map_source.rs +++ b/src/source_map_source.rs @@ -100,11 +100,11 @@ impl Source for SourceMapSource { self.value.len() } - fn map(&self, options: &MapOptions) -> Option { + fn map(&self, options: &MapOptions) -> Option> { if self.inner_source_map.is_none() { - return Some(self.source_map.clone()); + return Some(Cow::Borrowed(&self.source_map)); } - get_map(self, options) + get_map(self, options).map(Cow::Owned) } fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> { @@ -215,7 +215,9 @@ mod tests { name: "text", source_map: source_r_map.clone(), original_source: Some(inner_source.source().to_string()), - inner_source_map: inner_source.map(&MapOptions::default()), + inner_source_map: inner_source + .map(&MapOptions::default()) + .map(|map| map.into_owned()), remove_original_source: false, }); let sms2 = SourceMapSource::new(SourceMapSourceOptions { @@ -223,7 +225,9 @@ mod tests { name: "text", source_map: source_r_map, original_source: Some(inner_source.source().to_string()), - inner_source_map: inner_source.map(&MapOptions::default()), + inner_source_map: inner_source + .map(&MapOptions::default()) + .map(|map| map.into_owned()), remove_original_source: true, }); let expected_content = @@ -231,7 +235,7 @@ mod tests { assert_eq!(sms1.source(), expected_content); assert_eq!(sms2.source(), expected_content); assert_eq!( - sms1.map(&MapOptions::default()).unwrap(), + sms1.map(&MapOptions::default()).unwrap().into_owned(), SourceMap::from_json( r#"{ "mappings": "YAAAA,K,CAAMC;AACN,O,MAAU;ACCC,O,CAAM", @@ -247,7 +251,7 @@ mod tests { .unwrap(), ); assert_eq!( - sms2.map(&MapOptions::default()).unwrap(), + sms2.map(&MapOptions::default()).unwrap().into_owned(), SourceMap::from_json( r#"{ "mappings": "YAAAA,K,CAAMC;AACN,O,MAAU", @@ -300,7 +304,7 @@ mod tests { let source = ConcatSource::new(sources); assert_eq!(source.source(), "hi world\nhi world\nhi world\n"); assert_eq!( - source.map(&MapOptions::default()).unwrap(), + source.map(&MapOptions::default()).unwrap().into_owned(), SourceMap::from_json( r#"{ "mappings": "AAAA;;ACAA,CAAC,CAAI", @@ -313,7 +317,7 @@ mod tests { .unwrap() ); assert_eq!( - source.map(&MapOptions::new(false)).unwrap(), + source.map(&MapOptions::new(false)).unwrap().into_owned(), SourceMap::from_json( r#"{ "mappings": "AAAA;;ACAA", @@ -423,11 +427,15 @@ mod tests { } test_cached!(source, |s: &dyn Source| s.source().to_string()); - test_cached!(source, |s: &dyn Source| s.map(&MapOptions::default())); - test_cached!(source, |s: &dyn Source| s.map(&MapOptions { - columns: false, - final_source: true - })); + test_cached!(source, |s: &dyn Source| s + .map(&MapOptions::default()) + .map(|m| m.into_owned())); + test_cached!(source, |s: &dyn Source| s + .map(&MapOptions { + columns: false, + final_source: true + }) + .map(|m| m.into_owned())); } #[test] @@ -458,7 +466,7 @@ mod tests { remove_original_source: false, }); assert_eq!( - source.map(&MapOptions::default()).unwrap(), + source.map(&MapOptions::default()).unwrap().into_owned(), SourceMap::from_json( r#"{ "mappings": "AAAA", @@ -502,7 +510,7 @@ mod tests { assert_eq!(source.source(), "Message: H W!"); assert_eq!(source.size(), 13); assert_eq!( - source.map(&MapOptions::default()).unwrap(), + source.map(&MapOptions::default()).unwrap().into_owned(), SourceMap::from_json( r#"{ "mappings": "AAAAA,SCAA,ECAMC,C", @@ -555,7 +563,7 @@ mod tests { }); let map = source.map(&MapOptions::default()).unwrap(); assert_eq!( - map, + map.into_owned(), SourceMap::from_json( r#"{ "version": 3, @@ -574,7 +582,7 @@ mod tests { let source = SourceMapSource::new(WithoutOriginalOptions { value: "console.log('a')\n", name: "a.js", - source_map: original.map(&MapOptions::new(false)).unwrap(), + source_map: original.map(&MapOptions::new(false)).unwrap().into_owned(), }); let source = ConcatSource::new([ RawSource::from("\n").boxed(), @@ -608,7 +616,8 @@ mod tests { ) .unwrap(); let inner_source_map = - inner_source.map(&MapOptions::default()).map(|mut map| { + inner_source.map(&MapOptions::default()).map(|map| { + let mut map = map.into_owned(); map.set_source_root(Some("/path/to/folder/".to_string())); map }); @@ -621,7 +630,7 @@ mod tests { remove_original_source: false, }); assert_eq!( - sms.map(&MapOptions::default()).unwrap(), + sms.map(&MapOptions::default()).unwrap().into_owned(), SourceMap::from_json( r#"{ "mappings": "YAAAA,K,CAAMC;AACN,O,MAAU;ACCC,O,CAAM", @@ -668,7 +677,7 @@ mod tests { remove_original_source: false, }); assert_eq!( - source.map(&MapOptions::new(false)).unwrap(), + source.map(&MapOptions::new(false)).unwrap().into_owned(), SourceMap::from_json( r#"{ "mappings": "AAAA", @@ -710,7 +719,7 @@ mod tests { remove_original_source: false, }); assert_eq!( - source.map(&MapOptions::default()).unwrap(), + source.map(&MapOptions::default()).unwrap().into_owned(), SourceMap::from_json( r#"{ "version": 3, diff --git a/tests/compat_source.rs b/tests/compat_source.rs index 9079d26c..dc8a1ad4 100644 --- a/tests/compat_source.rs +++ b/tests/compat_source.rs @@ -24,8 +24,8 @@ impl Source for CompatSource { 42 } - fn map(&self, _options: &MapOptions) -> Option { - self.1.clone() + fn map(&self, _options: &MapOptions) -> Option> { + self.1.as_ref().map(Cow::Borrowed) } fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> { @@ -116,5 +116,5 @@ fn should_generate_correct_source_map() { .unwrap(); assert_eq!(source, expected_source); - assert_eq!(map, expected_source_map) + assert_eq!(map.into_owned(), expected_source_map) }