Skip to content

Commit e4e1522

Browse files
committed
feat(eval)!: fail when attempting to perform a bitwise operation with a numeric value that cannot be safely converted to i64
1 parent 12b0fd3 commit e4e1522

29 files changed

+147
-12
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## 0.4.0 (unreleased)
44

5+
### Breaking
6+
7+
- Performing bitwise operations with numeric values that cannot be safely
8+
converted to `i64` will fail.
9+
510
### Added
611

712
- New language features from Jsonnet 0.21:

rsjsonnet-front/src/report/eval.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,21 @@ pub(crate) fn render_error_kind(
375375
}
376376
.render(span_mgr, src_mgr, &mut out);
377377
}
378+
EvalErrorKind::NumberNotBitwiseSafe { span } => {
379+
Message {
380+
kind: MessageKind::Error,
381+
message: "numeric value cannot be safely used for bitwise operation".into(),
382+
labels: span
383+
.map(|span| MessageLabel {
384+
kind: LabelKind::Error,
385+
span,
386+
text: String::new(),
387+
})
388+
.into_iter()
389+
.collect(),
390+
}
391+
.render(span_mgr, src_mgr, &mut out);
392+
}
378393
EvalErrorKind::NumberOverflow { span } => {
379394
Message {
380395
kind: MessageKind::Error,
@@ -550,6 +565,7 @@ pub(crate) fn render_error_kind(
550565
}
551566
.render(span_mgr, src_mgr, &mut out);
552567
}
568+
553569
EvalErrorKind::CompareBooleanInequality => {
554570
Message {
555571
kind: MessageKind::Error,

rsjsonnet-lang/src/program/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ pub enum EvalErrorKind {
146146
lhs_type: EvalErrorValueType,
147147
rhs_type: EvalErrorValueType,
148148
},
149+
NumberNotBitwiseSafe {
150+
span: Option<SpanId>,
151+
},
149152
NumberOverflow {
150153
span: Option<SpanId>,
151154
},

rsjsonnet-lang/src/program/eval/expr.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -760,38 +760,42 @@ impl<'p> Evaluator<'_, 'p> {
760760
self.value_stack.push(ValueData::Number(r));
761761
}
762762
(ast::BinaryOp::Shl, ValueData::Number(lhs), ValueData::Number(rhs)) => {
763-
let lhs = lhs as i64;
763+
let lhs = self.safe_f64_to_i64(lhs, span)?;
764764
if rhs.is_sign_negative() {
765765
return Err(self.report_error(EvalErrorKind::ShiftByNegative { span }));
766766
}
767-
let rhs = rhs as i64;
768-
let r = lhs << (rhs & 63);
767+
let rhs = self.safe_f64_to_i64(rhs, span)?;
768+
let shift = rhs & 63;
769+
let r = lhs << shift;
770+
if r >> shift != lhs {
771+
return Err(self.report_error(EvalErrorKind::NumberNotBitwiseSafe { span }));
772+
}
769773
self.value_stack.push(ValueData::Number(r as f64));
770774
}
771775
(ast::BinaryOp::Shr, ValueData::Number(lhs), ValueData::Number(rhs)) => {
772-
let lhs = lhs as i64;
776+
let lhs = self.safe_f64_to_i64(lhs, span)?;
773777
if rhs.is_sign_negative() {
774778
return Err(self.report_error(EvalErrorKind::ShiftByNegative { span }));
775779
}
776-
let rhs = rhs as i64;
780+
let rhs = self.safe_f64_to_i64(rhs, span)?;
777781
let r = lhs >> (rhs & 63);
778782
self.value_stack.push(ValueData::Number(r as f64));
779783
}
780784
(ast::BinaryOp::BitwiseAnd, ValueData::Number(lhs), ValueData::Number(rhs)) => {
781-
let lhs = lhs as i64;
782-
let rhs = rhs as i64;
785+
let lhs = self.safe_f64_to_i64(lhs, span)?;
786+
let rhs = self.safe_f64_to_i64(rhs, span)?;
783787
let r = lhs & rhs;
784788
self.value_stack.push(ValueData::Number(r as f64));
785789
}
786790
(ast::BinaryOp::BitwiseOr, ValueData::Number(lhs), ValueData::Number(rhs)) => {
787-
let lhs = lhs as i64;
788-
let rhs = rhs as i64;
791+
let lhs = self.safe_f64_to_i64(lhs, span)?;
792+
let rhs = self.safe_f64_to_i64(rhs, span)?;
789793
let r = lhs | rhs;
790794
self.value_stack.push(ValueData::Number(r as f64));
791795
}
792796
(ast::BinaryOp::BitwiseXor, ValueData::Number(lhs), ValueData::Number(rhs)) => {
793-
let lhs = lhs as i64;
794-
let rhs = rhs as i64;
797+
let lhs = self.safe_f64_to_i64(lhs, span)?;
798+
let rhs = self.safe_f64_to_i64(rhs, span)?;
795799
let r = lhs ^ rhs;
796800
self.value_stack.push(ValueData::Number(r as f64));
797801
}

rsjsonnet-lang/src/program/eval/mod.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -886,7 +886,7 @@ impl<'p, 'a> Evaluator<'a, 'p> {
886886
self.value_stack.push(ValueData::Number(rhs));
887887
}
888888
(ast::UnaryOp::BitwiseNot, ValueData::Number(rhs)) => {
889-
let int = rhs as i64;
889+
let int = self.safe_f64_to_i64(rhs, Some(span))?;
890890
self.value_stack.push(ValueData::Number(!int as f64));
891891
}
892892
(ast::UnaryOp::LogicNot, ValueData::Bool(rhs)) => {
@@ -1646,6 +1646,17 @@ impl<'p, 'a> Evaluator<'a, 'p> {
16461646
}
16471647
}
16481648

1649+
fn safe_f64_to_i64(&mut self, value: f64, span: Option<SpanId>) -> EvalResult<i64> {
1650+
let max = (1u64 << 53) as f64;
1651+
let min = -max;
1652+
1653+
if value < min || value > max {
1654+
Err(self.report_error(EvalErrorKind::NumberNotBitwiseSafe { span }))
1655+
} else {
1656+
Ok(value as i64)
1657+
}
1658+
}
1659+
16491660
fn check_number_value(&mut self, value: f64, span: Option<SpanId>) -> EvalResult<()> {
16501661
match value.classify() {
16511662
std::num::FpCategory::Nan => Err(self.report_error(EvalErrorKind::NumberNan { span })),
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1e50 & 0
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
error: numeric value cannot be safely used for bitwise operation
2+
--> and_unsafe_lhs.jsonnet:1:1
3+
|
4+
1 | 1e50 & 0
5+
| ^^^^^^^^
6+
note: during top-level value evaluation
7+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0 & 1e50
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
error: numeric value cannot be safely used for bitwise operation
2+
--> and_unsafe_rhs.jsonnet:1:1
3+
|
4+
1 | 0 & 1e50
5+
| ^^^^^^^^
6+
note: during top-level value evaluation
7+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1e50 | 0

0 commit comments

Comments
 (0)