Skip to content

Commit 2893e8f

Browse files
committed
Fix crash when accessing property or indexed item on object returned from variable resolver.
1 parent da040a9 commit 2893e8f

File tree

3 files changed

+126
-37
lines changed

3 files changed

+126
-37
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ Rhai Release Notes
44
Version 1.18.0
55
==============
66

7+
Bug fixes
8+
---------
9+
10+
* The engine no longer crashes when accessing a property or indexed item from a shared value returned from a variables resolver.
11+
712
Deprecated API's
813
----------------
914

src/eval/chaining.rs

Lines changed: 105 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ impl Engine {
100100
}
101101

102102
/// Get the value at the indexed position of a base type.
103+
///
104+
/// # Panics
105+
///
106+
/// Panics if the target object is shared.
107+
///
108+
/// Shared objects should be handled (dereferenced) before calling this method.
103109
fn get_indexed_mut<'t>(
104110
&self,
105111
global: &mut GlobalRuntimeState,
@@ -430,10 +436,10 @@ impl Engine {
430436
let value = self
431437
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), lhs_expr)?
432438
.flatten();
433-
let obj_ptr = &mut value.into();
439+
let item_ptr = &mut value.into();
434440

435441
self.eval_dot_index_chain_raw(
436-
global, caches, scope2, this_ptr, lhs_expr, expr, obj_ptr, rhs, idx_values,
442+
global, caches, scope2, this_ptr, lhs_expr, expr, item_ptr, rhs, idx_values,
437443
None,
438444
)
439445
}
@@ -575,19 +581,30 @@ impl Engine {
575581
let idx_pos = x.lhs.start_position();
576582

577583
let (try_setter, result) = {
578-
let target = target.as_mut();
579-
let mut obj = self.get_indexed_mut(
580-
global, caches, target, idx_val, idx_pos, op_pos, false, true,
584+
let obj = target.as_mut();
585+
586+
#[cfg(not(feature = "no_closure"))]
587+
let mut target_guard;
588+
#[cfg(not(feature = "no_closure"))]
589+
let obj = if obj.is_shared() {
590+
target_guard = obj.write_lock::<Dynamic>().unwrap();
591+
&mut *target_guard
592+
} else {
593+
obj
594+
};
595+
596+
let mut item = self.get_indexed_mut(
597+
global, caches, obj, idx_val, idx_pos, op_pos, false, true,
581598
)?;
582-
let is_obj_temp_val = obj.is_temp_value();
583-
let obj_ptr = &mut obj;
599+
let is_item_temp_val = item.is_temp_value();
600+
let item_ptr = &mut item;
584601

585602
match self.eval_dot_index_chain_raw(
586-
global, caches, _scope, this_ptr, root, rhs, obj_ptr, &x.rhs,
603+
global, caches, _scope, this_ptr, root, rhs, item_ptr, &x.rhs,
587604
idx_values, new_val,
588605
) {
589-
Ok((result, true)) if is_obj_temp_val => {
590-
(Some(obj.take_or_clone()), (result, true))
606+
Ok((result, true)) if is_item_temp_val => {
607+
(Some(item.take_or_clone()), (result, true))
591608
}
592609
Ok(result) => (None, result),
593610
Err(err) => return Err(err),
@@ -617,19 +634,30 @@ impl Engine {
617634
#[cfg(feature = "debugging")]
618635
self.run_debugger(global, caches, _scope, this_ptr, parent)?;
619636

620-
let target = target.as_mut();
637+
let obj = target.as_mut();
638+
639+
#[cfg(not(feature = "no_closure"))]
640+
let mut target_guard;
641+
#[cfg(not(feature = "no_closure"))]
642+
let obj = if obj.is_shared() {
643+
target_guard = obj.write_lock::<Dynamic>().unwrap();
644+
&mut *target_guard
645+
} else {
646+
obj
647+
};
648+
621649
let idx_val = &mut idx_values.pop().unwrap();
622650
let idx = &mut idx_val.clone();
623651

624652
let try_setter = match self
625-
.get_indexed_mut(global, caches, target, idx, pos, op_pos, true, false)
653+
.get_indexed_mut(global, caches, obj, idx, pos, op_pos, true, false)
626654
{
627655
// Indexed value is not a temp value - update directly
628-
Ok(ref mut obj_ptr) => {
656+
Ok(ref mut item_ptr) => {
629657
self.eval_op_assignment(
630-
global, caches, op_info, root, obj_ptr, new_val,
658+
global, caches, op_info, root, item_ptr, new_val,
631659
)?;
632-
self.check_data_size(obj_ptr.as_ref(), op_info.position())?;
660+
self.check_data_size(item_ptr.as_ref(), op_info.position())?;
633661
None
634662
}
635663
// Indexed value cannot be referenced - use indexer
@@ -646,7 +674,7 @@ impl Engine {
646674

647675
// Call the index getter to get the current value
648676
if let Ok(val) =
649-
self.call_indexer_get(global, caches, target, idx, op_pos)
677+
self.call_indexer_get(global, caches, obj, idx, op_pos)
650678
{
651679
let mut val = val.into();
652680
// Run the op-assignment
@@ -663,7 +691,7 @@ impl Engine {
663691
let new_val = &mut new_val;
664692
// The return value of a indexer setter (usually `()`) is thrown away and not used.
665693
let _ = self.call_indexer_set(
666-
global, caches, target, idx_val, new_val, is_ref_mut, op_pos,
694+
global, caches, obj, idx_val, new_val, is_ref_mut, op_pos,
667695
)?;
668696
}
669697

@@ -674,13 +702,22 @@ impl Engine {
674702
#[cfg(feature = "debugging")]
675703
self.run_debugger(global, caches, _scope, this_ptr, parent)?;
676704

677-
let target = target.as_mut();
705+
let obj = target.as_mut();
706+
707+
#[cfg(not(feature = "no_closure"))]
708+
let mut target_guard;
709+
#[cfg(not(feature = "no_closure"))]
710+
let obj = if obj.is_shared() {
711+
target_guard = obj.write_lock::<Dynamic>().unwrap();
712+
&mut *target_guard
713+
} else {
714+
obj
715+
};
716+
678717
let idx_val = &mut idx_values.pop().unwrap();
679718

680-
self.get_indexed_mut(
681-
global, caches, target, idx_val, pos, op_pos, false, true,
682-
)
683-
.map(|v| (v.take_or_clone(), false))
719+
self.get_indexed_mut(global, caches, obj, idx_val, pos, op_pos, false, true)
720+
.map(|v| (v.take_or_clone(), false))
684721
}
685722
}
686723
}
@@ -731,13 +768,22 @@ impl Engine {
731768

732769
let index = &mut x.2.clone().into();
733770
{
734-
let target = target.as_mut();
735-
let val_target = &mut self.get_indexed_mut(
736-
global, caches, target, index, *pos, op_pos, true, false,
737-
)?;
738-
self.eval_op_assignment(
739-
global, caches, op_info, root, val_target, new_val,
771+
let obj = target.as_mut();
772+
773+
#[cfg(not(feature = "no_closure"))]
774+
let mut target_guard;
775+
#[cfg(not(feature = "no_closure"))]
776+
let obj = if obj.is_shared() {
777+
target_guard = obj.write_lock::<Dynamic>().unwrap();
778+
&mut *target_guard
779+
} else {
780+
obj
781+
};
782+
783+
let item = &mut self.get_indexed_mut(
784+
global, caches, obj, index, *pos, op_pos, true, false,
740785
)?;
786+
self.eval_op_assignment(global, caches, op_info, root, item, new_val)?;
741787
}
742788
self.check_data_size(target.source(), op_info.position())?;
743789
Ok((Dynamic::UNIT, true))
@@ -747,12 +793,23 @@ impl Engine {
747793
#[cfg(feature = "debugging")]
748794
self.run_debugger(global, caches, _scope, this_ptr, rhs)?;
749795

750-
let target = target.as_mut();
796+
let obj = target.as_mut();
797+
798+
#[cfg(not(feature = "no_closure"))]
799+
let mut target_guard;
800+
#[cfg(not(feature = "no_closure"))]
801+
let obj = if obj.is_shared() {
802+
target_guard = obj.write_lock::<Dynamic>().unwrap();
803+
&mut *target_guard
804+
} else {
805+
obj
806+
};
807+
751808
let index = &mut x.2.clone().into();
752-
let val = self.get_indexed_mut(
753-
global, caches, target, index, *pos, op_pos, false, false,
809+
let item = self.get_indexed_mut(
810+
global, caches, obj, index, *pos, op_pos, false, false,
754811
)?;
755-
Ok((val.take_or_clone(), false))
812+
Ok((item.take_or_clone(), false))
756813
}
757814
// xxx.id op= ???
758815
(Expr::Property(x, pos), Some((mut new_val, op_info)), false) => {
@@ -856,16 +913,27 @@ impl Engine {
856913
let _node = &x.lhs;
857914
let mut _this_ptr = this_ptr;
858915
let _tp = _this_ptr.as_deref_mut();
916+
#[cfg(not(feature = "no_closure"))]
917+
let mut target_guard;
859918

860-
let val_target = &mut match x.lhs {
919+
let item = &mut match x.lhs {
861920
Expr::Property(ref p, pos) => {
862921
#[cfg(feature = "debugging")]
863922
self.run_debugger(global, caches, _scope, _tp, _node)?;
864923

865-
let target = target.as_mut();
924+
let obj = target.as_mut();
925+
926+
#[cfg(not(feature = "no_closure"))]
927+
let obj = if obj.is_shared() {
928+
target_guard = obj.write_lock::<Dynamic>().unwrap();
929+
&mut *target_guard
930+
} else {
931+
obj
932+
};
933+
866934
let index = &mut p.2.clone().into();
867935
self.get_indexed_mut(
868-
global, caches, target, index, pos, op_pos, false, true,
936+
global, caches, obj, index, pos, op_pos, false, true,
869937
)?
870938
}
871939
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
@@ -902,8 +970,8 @@ impl Engine {
902970
};
903971

904972
self.eval_dot_index_chain_raw(
905-
global, caches, _scope, _this_ptr, root, rhs, val_target, &x.rhs,
906-
idx_values, new_val,
973+
global, caches, _scope, _this_ptr, root, rhs, item, &x.rhs, idx_values,
974+
new_val,
907975
)
908976
}
909977
// xxx.sub_lhs[expr] | xxx.sub_lhs.expr

tests/var_scope.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,22 @@ fn test_var_resolver() {
368368
assert_eq!(engine.eval::<INT>("HELLO = HELLO + 1; HELLO").unwrap(), 124);
369369
assert_eq!(engine.eval::<INT>("HELLO = HELLO * 2; HELLO").unwrap(), 248);
370370
assert_eq!(base.as_int().unwrap(), 248);
371+
372+
#[cfg(not(feature = "no_index"))]
373+
#[cfg(not(feature = "no_object"))]
374+
assert_eq!(
375+
engine
376+
.eval::<INT>(
377+
"
378+
HELLO = [1,2,3];
379+
HELLO[0] = #{a:#{foo:1}, b:1};
380+
HELLO[0].a.foo = 42;
381+
HELLO[0].a.foo
382+
"
383+
)
384+
.unwrap(),
385+
42
386+
);
371387
}
372388

373389
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "chameleon").unwrap(), 1);

0 commit comments

Comments
 (0)