Consolidate all unsafe code under one single file.
This commit is contained in:
parent
5c61827c7c
commit
5d5ceb4049
36
src/any.rs
36
src/any.rs
@ -1,10 +1,11 @@
|
|||||||
//! Helper module which defines the `Any` trait to to allow dynamic value handling.
|
//! Helper module which defines the `Any` trait to to allow dynamic value handling.
|
||||||
|
|
||||||
|
use crate::parser::INT;
|
||||||
|
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
|
|
||||||
use crate::parser::INT;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::parser::FLOAT;
|
use crate::parser::FLOAT;
|
||||||
|
|
||||||
@ -298,37 +299,6 @@ impl Default for Dynamic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cast a type into another type.
|
|
||||||
fn unsafe_try_cast<A: Any, B: Any>(a: A) -> Option<B> {
|
|
||||||
if TypeId::of::<B>() == a.type_id() {
|
|
||||||
// SAFETY: Just checked we have the right type. We explicitly forget the
|
|
||||||
// value immediately after moving out, removing any chance of a destructor
|
|
||||||
// running or value otherwise being used again.
|
|
||||||
unsafe {
|
|
||||||
let ret: B = ptr::read(&a as *const _ as *const B);
|
|
||||||
mem::forget(a);
|
|
||||||
Some(ret)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cast a Boxed type into another type.
|
|
||||||
fn unsafe_cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, Box<X>> {
|
|
||||||
// Only allow casting to the exact same type
|
|
||||||
if TypeId::of::<X>() == TypeId::of::<T>() {
|
|
||||||
// SAFETY: just checked whether we are pointing to the correct type
|
|
||||||
unsafe {
|
|
||||||
let raw: *mut dyn Any = Box::into_raw(item as Box<dyn Any>);
|
|
||||||
Ok(Box::from_raw(raw as *mut T))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Return the consumed item for chaining.
|
|
||||||
Err(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Dynamic {
|
impl Dynamic {
|
||||||
/// Create a `Dynamic` from any type. A `Dynamic` value is simply returned as is.
|
/// Create a `Dynamic` from any type. A `Dynamic` value is simply returned as is.
|
||||||
///
|
///
|
||||||
|
@ -8,6 +8,7 @@ use crate::module::Module;
|
|||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::packages::{CorePackage, Package, PackageLibrary, PackagesCollection, StandardPackage};
|
use crate::packages::{CorePackage, Package, PackageLibrary, PackagesCollection, StandardPackage};
|
||||||
use crate::parser::{Expr, FnAccess, FnDef, ReturnType, SharedFnDef, Stmt, AST};
|
use crate::parser::{Expr, FnAccess, FnDef, ReturnType, SharedFnDef, Stmt, AST};
|
||||||
|
use crate::r#unsafe::unsafe_cast_var_name;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
@ -127,6 +128,11 @@ impl<T: Into<Dynamic>> From<T> for Target<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A type that holds all the current states of the Engine.
|
/// A type that holds all the current states of the Engine.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This type uses some unsafe code, mainly for avoiding cloning of local variable names via
|
||||||
|
/// direct lifetime casting.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct State<'a> {
|
pub struct State<'a> {
|
||||||
/// Global script-defined functions.
|
/// Global script-defined functions.
|
||||||
@ -265,24 +271,6 @@ impl DerefMut for FunctionsLib {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A dangerous function that blindly casts a `&str` from one lifetime to a `Cow<str>` of
|
|
||||||
/// another lifetime. This is mainly used to let us push a block-local variable into the
|
|
||||||
/// current `Scope` without cloning the variable name. Doing this is safe because all local
|
|
||||||
/// variables in the `Scope` are cleared out before existing the block.
|
|
||||||
///
|
|
||||||
/// Force-casting a local variable lifetime to the current `Scope`'s larger lifetime saves
|
|
||||||
/// on allocations and string cloning, thus avoids us having to maintain a chain of `Scope`'s.
|
|
||||||
fn unsafe_cast_var_name<'s>(name: &str, state: &State) -> Cow<'s, str> {
|
|
||||||
// If not at global level, we can force-cast
|
|
||||||
if state.scope_level > 0 {
|
|
||||||
// WARNING - force-cast the variable name into the scope's lifetime to avoid cloning it
|
|
||||||
// this is safe because all local variables are cleared at the end of the block
|
|
||||||
unsafe { mem::transmute::<_, &'s str>(name) }.into()
|
|
||||||
} else {
|
|
||||||
name.to_string().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Rhai main scripting engine.
|
/// Rhai main scripting engine.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -85,6 +85,7 @@ mod result;
|
|||||||
mod scope;
|
mod scope;
|
||||||
mod stdlib;
|
mod stdlib;
|
||||||
mod token;
|
mod token;
|
||||||
|
mod r#unsafe;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
pub use any::Dynamic;
|
pub use any::Dynamic;
|
||||||
|
68
src/unsafe.rs
Normal file
68
src/unsafe.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
//! A module containing all unsafe code.
|
||||||
|
|
||||||
|
use crate::any::Variant;
|
||||||
|
use crate::engine::State;
|
||||||
|
use crate::utils::StaticVec;
|
||||||
|
|
||||||
|
use crate::stdlib::{
|
||||||
|
any::{Any, TypeId},
|
||||||
|
borrow::Cow,
|
||||||
|
boxed::Box,
|
||||||
|
mem, ptr,
|
||||||
|
string::ToString,
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Cast a type into another type.
|
||||||
|
pub fn unsafe_try_cast<A: Any, B: Any>(a: A) -> Option<B> {
|
||||||
|
if TypeId::of::<B>() == a.type_id() {
|
||||||
|
// SAFETY: Just checked we have the right type. We explicitly forget the
|
||||||
|
// value immediately after moving out, removing any chance of a destructor
|
||||||
|
// running or value otherwise being used again.
|
||||||
|
unsafe {
|
||||||
|
let ret: B = ptr::read(&a as *const _ as *const B);
|
||||||
|
mem::forget(a);
|
||||||
|
Some(ret)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cast a Boxed type into another type.
|
||||||
|
pub fn unsafe_cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, Box<X>> {
|
||||||
|
// Only allow casting to the exact same type
|
||||||
|
if TypeId::of::<X>() == TypeId::of::<T>() {
|
||||||
|
// SAFETY: just checked whether we are pointing to the correct type
|
||||||
|
unsafe {
|
||||||
|
let raw: *mut dyn Any = Box::into_raw(item as Box<dyn Any>);
|
||||||
|
Ok(Box::from_raw(raw as *mut T))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Return the consumed item for chaining.
|
||||||
|
Err(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A dangerous function that blindly casts a `&str` from one lifetime to a `Cow<str>` of
|
||||||
|
/// another lifetime. This is mainly used to let us push a block-local variable into the
|
||||||
|
/// current `Scope` without cloning the variable name. Doing this is safe because all local
|
||||||
|
/// variables in the `Scope` are cleared out before existing the block.
|
||||||
|
///
|
||||||
|
/// Force-casting a local variable lifetime to the current `Scope`'s larger lifetime saves
|
||||||
|
/// on allocations and string cloning, thus avoids us having to maintain a chain of `Scope`'s.
|
||||||
|
pub fn unsafe_cast_var_name<'s>(name: &str, state: &State) -> Cow<'s, str> {
|
||||||
|
// If not at global level, we can force-cast
|
||||||
|
if state.scope_level > 0 {
|
||||||
|
// WARNING - force-cast the variable name into the scope's lifetime to avoid cloning it
|
||||||
|
// this is safe because all local variables are cleared at the end of the block
|
||||||
|
unsafe { mem::transmute::<_, &'s str>(name) }.into()
|
||||||
|
} else {
|
||||||
|
name.to_string().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provide a type instance that is memory-zeroed.
|
||||||
|
pub fn unsafe_zeroed<T>() -> T {
|
||||||
|
unsafe { mem::MaybeUninit::zeroed().assume_init() }
|
||||||
|
}
|
26
src/utils.rs
26
src/utils.rs
@ -1,5 +1,7 @@
|
|||||||
//! Module containing various utility types and functions.
|
//! Module containing various utility types and functions.
|
||||||
|
|
||||||
|
use crate::r#unsafe::unsafe_zeroed;
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
fmt,
|
fmt,
|
||||||
@ -73,16 +75,6 @@ impl<T: PartialEq> PartialEq for StaticVec<T> {
|
|||||||
|
|
||||||
impl<T: Eq> Eq for StaticVec<T> {}
|
impl<T: Eq> Eq for StaticVec<T> {}
|
||||||
|
|
||||||
impl<T> Default for StaticVec<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
len: 0,
|
|
||||||
list: unsafe { mem::MaybeUninit::zeroed().assume_init() },
|
|
||||||
more: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> FromIterator<T> for StaticVec<T> {
|
impl<T> FromIterator<T> for StaticVec<T> {
|
||||||
fn from_iter<X: IntoIterator<Item = T>>(iter: X) -> Self {
|
fn from_iter<X: IntoIterator<Item = T>>(iter: X) -> Self {
|
||||||
let mut vec = StaticVec::new();
|
let mut vec = StaticVec::new();
|
||||||
@ -95,6 +87,16 @@ impl<T> FromIterator<T> for StaticVec<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Default for StaticVec<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
len: 0,
|
||||||
|
list: unsafe_zeroed(),
|
||||||
|
more: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> StaticVec<T> {
|
impl<T> StaticVec<T> {
|
||||||
/// Create a new `StaticVec`.
|
/// Create a new `StaticVec`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@ -105,7 +107,7 @@ impl<T> StaticVec<T> {
|
|||||||
if self.len == self.list.len() {
|
if self.len == self.list.len() {
|
||||||
// Move the fixed list to the Vec
|
// Move the fixed list to the Vec
|
||||||
for x in 0..self.list.len() {
|
for x in 0..self.list.len() {
|
||||||
let def_val: T = unsafe { mem::MaybeUninit::zeroed().assume_init() };
|
let def_val: T = unsafe_zeroed();
|
||||||
self.more
|
self.more
|
||||||
.push(mem::replace(self.list.get_mut(x).unwrap(), def_val));
|
.push(mem::replace(self.list.get_mut(x).unwrap(), def_val));
|
||||||
}
|
}
|
||||||
@ -126,7 +128,7 @@ impl<T> StaticVec<T> {
|
|||||||
let result = if self.len <= 0 {
|
let result = if self.len <= 0 {
|
||||||
panic!("nothing to pop!")
|
panic!("nothing to pop!")
|
||||||
} else if self.len <= self.list.len() {
|
} else if self.len <= self.list.len() {
|
||||||
let def_val: T = unsafe { mem::MaybeUninit::zeroed().assume_init() };
|
let def_val: T = unsafe_zeroed();
|
||||||
mem::replace(self.list.get_mut(self.len - 1).unwrap(), def_val)
|
mem::replace(self.list.get_mut(self.len - 1).unwrap(), def_val)
|
||||||
} else {
|
} else {
|
||||||
let r = self.more.pop().unwrap();
|
let r = self.more.pop().unwrap();
|
||||||
|
Loading…
Reference in New Issue
Block a user