From e3c8e12f989651c51d3ae430d888bee7eb058207 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Sun, 3 Nov 2024 10:05:22 -0800 Subject: [PATCH] Fix stack overflows in calc Fixes #827, fixes #791, fixes #611, closes #832 --- src/lib.rs | 21 ++++++++++++++ src/values/calc.rs | 68 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 21529f03..3421a323 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7847,6 +7847,27 @@ mod tests { ".foo{transform:rotateX(-40deg)rotateY(50deg)}", ); minify_test(".foo { width: calc(10px * mod(18, 5)) }", ".foo{width:30px}"); + + minify_test( + ".foo { width: calc(100% - 30px - 0) }", + ".foo{width:calc(100% - 30px - 0)}", + ); + minify_test( + ".foo { width: calc(100% - 30px - 1 - 2) }", + ".foo{width:calc(100% - 30px - 3)}", + ); + minify_test( + ".foo { width: calc(1 - 2 - 100% - 30px) }", + ".foo{width:calc(-1 - 100% - 30px)}", + ); + minify_test( + ".foo { width: calc(2 * min(1px, 1vmin) - min(1px, 1vmin)); }", + ".foo{width:calc(2*min(1px,1vmin) - min(1px,1vmin))}", + ); + minify_test( + ".foo { width: calc(100% - clamp(1.125rem, 1.25vw, 1.2375rem) - clamp(1.125rem, 1.25vw, 1.2375rem)); }", + ".foo{width:calc(100% - clamp(1.125rem,1.25vw,1.2375rem) - clamp(1.125rem,1.25vw,1.2375rem))}", + ); } #[test] diff --git a/src/values/calc.rs b/src/values/calc.rs index 742c5184..22610e58 100644 --- a/src/values/calc.rs +++ b/src/values/calc.rs @@ -908,11 +908,31 @@ impl> + std::convert::From> match (self, other) { (Calc::Value(a), Calc::Value(b)) => (a.add(*b)).into(), (Calc::Number(a), Calc::Number(b)) => Calc::Number(a + b), + (Calc::Sum(a, b), Calc::Number(c)) => { + if let Calc::Number(a) = *a { + Calc::Sum(Box::new(Calc::Number(a + c)), b) + } else if let Calc::Number(b) = *b { + Calc::Sum(a, Box::new(Calc::Number(b + c))) + } else { + Calc::Sum(Box::new(Calc::Sum(a, b)), Box::new(Calc::Number(c))) + } + } + (Calc::Number(a), Calc::Sum(b, c)) => { + if let Calc::Number(b) = *b { + Calc::Sum(Box::new(Calc::Number(a + b)), c) + } else if let Calc::Number(c) = *c { + Calc::Sum(Box::new(Calc::Number(a + c)), b) + } else { + Calc::Sum(Box::new(Calc::Number(a)), Box::new(Calc::Sum(b, c))) + } + } + (a @ Calc::Product(..), b) => Calc::Sum(Box::new(a), Box::new(b)), + (a, b @ Calc::Product(..)) => Calc::Sum(Box::new(a), Box::new(b)), (Calc::Value(a), b) => (a.add(V::from(b))).into(), (a, Calc::Value(b)) => (V::from(a).add(*b)).into(), (Calc::Function(a), b) => Calc::Sum(Box::new(Calc::Function(a)), Box::new(b)), (a, Calc::Function(b)) => Calc::Sum(Box::new(a), Box::new(Calc::Function(b))), - (a, b) => V::from(a).add(V::from(b)).into(), + (a @ Calc::Sum(..), b @ Calc::Sum(..)) => V::from(a).add(V::from(b)).into(), } } } @@ -966,6 +986,52 @@ impl TrySign for Calc { match self { Calc::Number(v) => v.try_sign(), Calc::Value(v) => v.try_sign(), + Calc::Product(c, v) => v.try_sign().map(|s| s * c.sign()), + Calc::Function(f) => f.try_sign(), + _ => None, + } + } +} + +impl TrySign for MathFunction { + fn try_sign(&self) -> Option { + match self { + MathFunction::Abs(_) => Some(1.0), + MathFunction::Max(values) | MathFunction::Min(values) => { + let mut iter = values.iter(); + if let Some(sign) = iter.next().and_then(|f| f.try_sign()) { + for value in iter { + if let Some(s) = value.try_sign() { + if s != sign { + return None; + } + } else { + return None; + } + } + return Some(sign); + } else { + return None; + } + } + MathFunction::Clamp(a, b, c) => { + if let (Some(a), Some(b), Some(c)) = (a.try_sign(), b.try_sign(), c.try_sign()) { + if a == b && b == c { + return Some(a); + } + } + return None; + } + MathFunction::Round(_, a, b) => { + if let (Some(a), Some(b)) = (a.try_sign(), b.try_sign()) { + if a == b { + return Some(a); + } + } + return None; + } + MathFunction::Sign(v) => v.try_sign(), + MathFunction::Calc(v) => v.try_sign(), _ => None, } }