diff --git a/CHANGELOG.md b/CHANGELOG.md index e594d41e..b77d3dea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Bug fixes Breaking changes ---------------- +* `Dynamic::as_str` is removed because it does not properly handle shared values. * Zero step in the `range` function now raises an error instead of creating an infinite stream. * Error variable captured by `catch` is now an _object map_ containing error fields. * `EvalAltResult::clear_position` is renamed `EvalAltResult::take_position` and returns the position taken. diff --git a/src/dynamic.rs b/src/dynamic.rs index b4d569f0..020ed7e8 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -1526,15 +1526,18 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Cast the [`Dynamic`] as a [`String`] and return the string slice. + /// Cast the [`Dynamic`] as an [`ImmutableString`] and return it. /// Returns the name of the actual type if the cast fails. /// - /// Fails if `self` is _shared_. + /// # Panics + /// + /// Panics if the value is shared. #[inline(always)] - pub fn as_str(&self) -> Result<&str, &'static str> { + pub(crate) fn as_str(&self) -> Result<&str, &'static str> { match &self.0 { Union::Str(s, _) => Ok(s), - Union::FnPtr(f, _) => Ok(f.fn_name()), + #[cfg(not(feature = "no_closure"))] + Union::Shared(_, _) => panic!("as_str() cannot be called on shared values"), _ => Err(self.type_name()), } } diff --git a/src/fn_call.rs b/src/fn_call.rs index 6a3db275..2250b5df 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -353,17 +353,17 @@ impl Engine { // See if the function match print/debug (which requires special processing) return Ok(match fn_name { KEYWORD_PRINT => { - let text = result.as_str().map_err(|typ| { + let text = result.take_immutable_string().map_err(|typ| { EvalAltResult::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), typ.into(), pos, ) })?; - ((self.print)(text).into(), false) + ((self.print)(&text).into(), false) } KEYWORD_DEBUG => { - let text = result.as_str().map_err(|typ| { + let text = result.take_immutable_string().map_err(|typ| { EvalAltResult::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), typ.into(), @@ -371,7 +371,7 @@ impl Engine { ) })?; let source = state.source.as_ref().map(|s| s.as_str()); - ((self.debug)(text, source, pos).into(), false) + ((self.debug)(&text, source, pos).into(), false) } _ => (result, func.is_method()), }); @@ -677,7 +677,7 @@ impl Engine { crate::engine::KEYWORD_IS_DEF_FN if args.len() == 2 && args[0].is::() && args[1].is::() => { - let fn_name = args[0].as_str().unwrap(); + let fn_name = mem::take(args[0]).take_immutable_string().unwrap(); let num_params = args[1].as_int().unwrap(); return Ok(( @@ -685,7 +685,7 @@ impl Engine { Dynamic::FALSE } else { let hash_script = - calc_script_fn_hash(empty(), fn_name, num_params as usize); + calc_script_fn_hash(empty(), &fn_name, num_params as usize); self.has_override(Some(mods), state, lib, None, hash_script) .into() }, @@ -1131,7 +1131,7 @@ impl Engine { crate::engine::KEYWORD_IS_DEF_FN if args_expr.len() == 2 => { let fn_name = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; - let fn_name = fn_name.as_str().map_err(|err| { + let fn_name = fn_name.take_immutable_string().map_err(|err| { self.make_type_mismatch_err::(err, args_expr[0].position()) })?; let num_params = @@ -1143,7 +1143,7 @@ impl Engine { return Ok(if num_params < 0 { Dynamic::FALSE } else { - let hash_script = calc_script_fn_hash(empty(), fn_name, num_params as usize); + let hash_script = calc_script_fn_hash(empty(), &fn_name, num_params as usize); self.has_override(Some(mods), state, lib, None, hash_script) .into() }); @@ -1153,10 +1153,10 @@ impl Engine { KEYWORD_IS_DEF_VAR if args_expr.len() == 1 => { let var_name = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; - let var_name = var_name.as_str().map_err(|err| { + let var_name = var_name.take_immutable_string().map_err(|err| { self.make_type_mismatch_err::(err, args_expr[0].position()) })?; - return Ok(scope.contains(var_name).into()); + return Ok(scope.contains(&var_name).into()); } // Handle eval() @@ -1168,7 +1168,7 @@ impl Engine { let prev_len = scope.len(); let script = self.eval_expr(scope, mods, state, lib, this_ptr, script_expr, level)?; - let script = script.as_str().map_err(|typ| { + let script = script.take_immutable_string().map_err(|typ| { self.make_type_mismatch_err::(typ, script_pos) })?; let result = self.eval_script_expr_in_place( @@ -1176,7 +1176,7 @@ impl Engine { mods, state, lib, - script, + &script, script_pos, level + 1, ); diff --git a/src/result.rs b/src/result.rs index 1376dbcb..49743d1d 100644 --- a/src/result.rs +++ b/src/result.rs @@ -190,7 +190,7 @@ impl fmt::Display for EvalAltResult { | Self::ErrorTerminated(_, _) => f.write_str(desc)?, Self::ErrorRuntime(d, _) if d.is::() => { - let s = d.as_str().unwrap(); + let s = &*d.read_lock::().unwrap(); write!(f, "{}: {}", desc, if s.is_empty() { desc } else { s })? } Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?, diff --git a/tests/closures.rs b/tests/closures.rs index 017200d6..d64cccd7 100644 --- a/tests/closures.rs +++ b/tests/closures.rs @@ -300,7 +300,7 @@ fn test_closures_external() -> Result<(), Box> { // Closure 'f' captures: the engine, the AST, and the curried function pointer let f = move |x: INT| fn_ptr.call_dynamic(context, None, [x.into()]); - assert_eq!(f(42)?.as_str(), Ok("hello42")); + assert_eq!(f(42)?.take_string(), Ok("hello42".to_string())); Ok(()) }