Skip to content

Commit d5afecd

Browse files
committed
Enhance optimizer to support constant map expressions
Signed-off-by: Pierre-Henri Symoneaux <[email protected]>
1 parent fe94269 commit d5afecd

File tree

2 files changed

+50
-0
lines changed

2 files changed

+50
-0
lines changed

src/optimizer.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,12 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
954954
*expr = mem::take(&mut m.0).into_iter().find(|(x, ..)| x.name == p.2)
955955
.map_or_else(|| Expr::Unit(*pos), |(.., mut expr)| { expr.set_position(*pos); expr });
956956
}
957+
// var.property where var is a constant map
958+
(Expr::Variable(v, _, pos) , Expr::Property(p, ..)) if state.propagate_constants && state.find_literal_constant(&v.1).map_or(false,Dynamic::is_map) => {
959+
let v = state.find_literal_constant(&v.1).unwrap().as_map_ref().unwrap().get(p.2.as_str()).cloned().unwrap_or(Dynamic::UNIT);
960+
*expr = Expr::from_dynamic(v, *pos);
961+
state.set_dirty();
962+
},
957963
// var.rhs or this.rhs
958964
(Expr::Variable(..) | Expr::ThisPtr(..), rhs) => optimize_expr(rhs, state, true),
959965
// const.type_of()
@@ -1015,6 +1021,18 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
10151021
*expr = mem::take(&mut m.0).into_iter().find(|(x, ..)| x.name == s)
10161022
.map_or_else(|| Expr::Unit(*pos), |(.., mut expr)| { expr.set_position(*pos); expr });
10171023
}
1024+
#[cfg(not(feature = "no_object"))]
1025+
(Expr::DynamicConstant(cst, pos ), Expr::StringConstant(s, ..)) if cst.is_map() => {
1026+
// Constant map - promote the indexed item.
1027+
state.set_dirty();
1028+
let mut cst = mem::take(cst);
1029+
*expr = cst.as_map_mut().unwrap()
1030+
.remove(s.as_str())
1031+
.map_or_else(
1032+
|| Expr::Unit(*pos),
1033+
|v| Expr::from_dynamic(v, *pos),
1034+
);
1035+
}
10181036
// int[int]
10191037
(Expr::IntegerConstant(n, pos), Expr::IntegerConstant(i, ..)) if usize::try_from(*i).map(|x| x < crate::INT_BITS).unwrap_or(false) => {
10201038
// Bit-field literal indexing - get the bit
@@ -1039,6 +1057,13 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
10391057
state.set_dirty();
10401058
*expr = Expr::CharConstant(s.chars().rev().nth(usize::try_from(i.unsigned_abs()).unwrap() - 1).unwrap(), *pos);
10411059
}
1060+
// var[property] where var is a constant map variable
1061+
#[cfg(not(feature = "no_object"))]
1062+
(Expr::Variable(v, _, pos) , Expr::StringConstant(s, ..)) if state.propagate_constants && state.find_literal_constant(&v.1).map_or(false, Dynamic::is_map) => {
1063+
let v = state.find_literal_constant(&v.1).unwrap().as_map_ref().unwrap().get(s.as_str()).cloned().unwrap_or(Dynamic::UNIT);
1064+
*expr = Expr::from_dynamic(v, *pos);
1065+
state.set_dirty();
1066+
},
10421067
// var[rhs] or this[rhs]
10431068
(Expr::Variable(..) | Expr::ThisPtr(..), rhs) => optimize_expr(rhs, state, true),
10441069
// lhs[rhs]

tests/optimizer.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,28 @@ fn test_optimizer_volatile() {
223223
// Make sure the call is optimized away
224224
assert!(!text_ast.contains(r#"name: "foo""#));
225225
}
226+
227+
#[cfg(not(feature = "no_object"))]
228+
#[cfg(not(feature = "no_index"))]
229+
#[test]
230+
fn test_optimizer_const_map() {
231+
let mut engine = Engine::new();
232+
engine.set_optimization_level(OptimizationLevel::Simple);
233+
234+
let mut scope = Scope::new();
235+
let mut map = rhai::Map::new();
236+
map.insert("a".into(), 42.into());
237+
scope.push_constant_dynamic("my_map", map.into());
238+
scope.push_constant_dynamic("x", "a".into());
239+
240+
let exprs = [r#"my_map["a"] == 42"#, r#"my_map.a == 42"#, r#"my_map[x] == 42"#, r#"#{a: 42}[x] == 42"#, r#"#{a: 42}["a"] == 42"#, r#"#{a: 42}.a == 42"#];
241+
for expr in exprs {
242+
let ast = engine.compile_expression_with_scope(&scope, expr).expect(&format!("Failed to compile expression: {expr}").as_str());
243+
244+
let ast_text = format!("{ast:?}");
245+
assert!(["Index", "Dot", "FnCall"].iter().all(|p| !ast_text.contains(p)), "Expression was not optimized: {} => {}", expr, ast_text);
246+
247+
let res = engine.eval_ast::<bool>(&ast).expect(&format!("Failed to evaluate expression: {expr}"));
248+
assert!(res, "Constant map optimization failed for expression: {} => {:?}", expr, ast);
249+
}
250+
}

0 commit comments

Comments
 (0)