Add Dynamic::NULL to simplify this pointer binding.

This commit is contained in:
Stephen Chung 2022-11-08 16:16:42 +08:00
parent 6053aa1641
commit 8f128f37f0
16 changed files with 126 additions and 77 deletions

View File

@ -250,7 +250,9 @@ impl Engine {
) -> RhaiResult { ) -> RhaiResult {
let statements = ast.statements(); let statements = ast.statements();
let lib = &[AsRef::<SharedModule>::as_ref(ast).clone()]; let lib = &[AsRef::<SharedModule>::as_ref(ast).clone()];
let mut this_ptr = this_ptr;
let mut no_this_ptr = Dynamic::NULL;
let mut this_ptr = this_ptr.unwrap_or(&mut no_this_ptr);
let orig_scope_len = scope.len(); let orig_scope_len = scope.len();

View File

@ -198,8 +198,10 @@ impl Engine {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
AsRef::<crate::SharedModule>::as_ref(ast).clone(), AsRef::<crate::SharedModule>::as_ref(ast).clone(),
]; ];
let mut this = Dynamic::NULL;
let node = &crate::ast::Stmt::Noop(Position::NONE); let node = &crate::ast::Stmt::Noop(Position::NONE);
self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?;
self.run_debugger(global, caches, lib, 0, scope, &mut this, node)?;
} }
let typ = self.map_type_name(result.type_name()); let typ = self.map_type_name(result.type_name());

View File

@ -141,8 +141,9 @@ impl Engine {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
AsRef::<crate::SharedModule>::as_ref(ast).clone(), AsRef::<crate::SharedModule>::as_ref(ast).clone(),
]; ];
let mut this = crate::Dynamic::NULL;
let node = &crate::ast::Stmt::Noop(crate::Position::NONE); let node = &crate::ast::Stmt::Noop(crate::Position::NONE);
self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; self.run_debugger(global, caches, lib, 0, scope, &mut this, node)?;
} }
Ok(()) Ok(())

View File

@ -45,7 +45,7 @@ impl Engine {
caches: &mut Caches, caches: &mut Caches,
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Dynamic,
target: &mut Target, target: &mut Target,
root: (&str, Position), root: (&str, Position),
_parent: &Expr, _parent: &Expr,
@ -573,7 +573,7 @@ impl Engine {
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Dynamic,
expr: &Expr, expr: &Expr,
new_val: &mut Option<(Dynamic, &OpAssignment)>, new_val: &mut Option<(Dynamic, &OpAssignment)>,
) -> RhaiResult { ) -> RhaiResult {
@ -630,9 +630,10 @@ impl Engine {
let obj_ptr = &mut target; let obj_ptr = &mut target;
let root = (x.3.as_str(), *var_pos); let root = (x.3.as_str(), *var_pos);
let mut this = Dynamic::NULL;
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
global, caches, lib, level, &mut None, obj_ptr, root, expr, options, rhs, global, caches, lib, level, &mut this, obj_ptr, root, expr, options, rhs,
idx_values, chain_type, new_val, idx_values, chain_type, new_val,
) )
} }
@ -664,7 +665,7 @@ impl Engine {
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Dynamic,
expr: &Expr, expr: &Expr,
parent_options: ASTFlags, parent_options: ASTFlags,
parent_chain_type: ChainType, parent_chain_type: ChainType,

View File

@ -416,7 +416,7 @@ impl Engine {
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Dynamic,
node: impl Into<ASTNode<'a>>, node: impl Into<ASTNode<'a>>,
) -> RhaiResultOf<()> { ) -> RhaiResultOf<()> {
if self.debugger.is_some() { if self.debugger.is_some() {
@ -443,7 +443,7 @@ impl Engine {
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Dynamic,
node: impl Into<ASTNode<'a>>, node: impl Into<ASTNode<'a>>,
) -> RhaiResultOf<Option<DebuggerStatus>> { ) -> RhaiResultOf<Option<DebuggerStatus>> {
if self.debugger.is_some() { if self.debugger.is_some() {
@ -466,7 +466,7 @@ impl Engine {
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Dynamic,
node: impl Into<ASTNode<'a>>, node: impl Into<ASTNode<'a>>,
) -> RhaiResultOf<Option<DebuggerStatus>> { ) -> RhaiResultOf<Option<DebuggerStatus>> {
let node = node.into(); let node = node.into();
@ -513,7 +513,7 @@ impl Engine {
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Dynamic,
node: ASTNode<'a>, node: ASTNode<'a>,
event: DebuggerEvent, event: DebuggerEvent,
) -> Result<Option<DebuggerStatus>, Box<crate::EvalAltResult>> { ) -> Result<Option<DebuggerStatus>, Box<crate::EvalAltResult>> {

View File

@ -8,7 +8,7 @@ use std::prelude::v1::*;
/// Context of a script evaluation process. /// Context of a script evaluation process.
#[derive(Debug)] #[derive(Debug)]
#[allow(dead_code)] #[allow(dead_code)]
pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
/// The current [`Engine`]. /// The current [`Engine`].
engine: &'a Engine, engine: &'a Engine,
/// The current [`Scope`]. /// The current [`Scope`].
@ -20,12 +20,12 @@ pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> {
/// The current stack of imported [modules][Module]. /// The current stack of imported [modules][Module].
lib: &'a [SharedModule], lib: &'a [SharedModule],
/// The current bound `this` pointer, if any. /// The current bound `this` pointer, if any.
this_ptr: &'t mut Option<&'pt mut Dynamic>, this_ptr: &'t mut Dynamic,
/// The current nesting level of function calls. /// The current nesting level of function calls.
level: usize, level: usize,
} }
impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
/// Create a new [`EvalContext`]. /// Create a new [`EvalContext`].
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
@ -36,7 +36,7 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> {
lib: &'a [SharedModule], lib: &'a [SharedModule],
level: usize, level: usize,
scope: &'s mut Scope<'ps>, scope: &'s mut Scope<'ps>,
this_ptr: &'t mut Option<&'pt mut Dynamic>, this_ptr: &'t mut Dynamic,
) -> Self { ) -> Self {
Self { Self {
engine, engine,
@ -104,8 +104,8 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> {
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn global_runtime_state_mut(&mut self) -> &mut &'g mut GlobalRuntimeState { pub fn global_runtime_state_mut(&mut self) -> &mut GlobalRuntimeState {
&mut self.global self.global
} }
/// Get an iterator over the namespaces containing definition of all script-defined functions. /// Get an iterator over the namespaces containing definition of all script-defined functions.
#[inline] #[inline]
@ -121,16 +121,24 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> {
self.lib self.lib
} }
/// The current bound `this` pointer, if any. /// The current bound `this` pointer, if any.
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn this_ptr(&self) -> Option<&Dynamic> { pub fn this_ptr(&self) -> Option<&Dynamic> {
self.this_ptr.as_ref().map(|v| &**v) if self.this_ptr.is_null() {
None
} else {
Some(self.this_ptr)
}
} }
/// Mutable reference to the current bound `this` pointer, if any. /// Mutable reference to the current bound `this` pointer, if any.
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn this_ptr_mut(&mut self) -> &mut Option<&'pt mut Dynamic> { pub fn this_ptr_mut(&mut self) -> Option<&mut Dynamic> {
self.this_ptr if self.this_ptr.is_null() {
None
} else {
Some(self.this_ptr)
}
} }
/// The current nesting level of function calls. /// The current nesting level of function calls.
#[inline(always)] #[inline(always)]

View File

@ -54,7 +54,7 @@ impl Engine {
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
scope: &'s mut Scope, scope: &'s mut Scope,
this_ptr: &'s mut Option<&mut Dynamic>, this_ptr: &'s mut Dynamic,
expr: &Expr, expr: &Expr,
) -> RhaiResultOf<(Target<'s>, Position)> { ) -> RhaiResultOf<(Target<'s>, Position)> {
match expr { match expr {
@ -140,7 +140,7 @@ impl Engine {
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
scope: &'s mut Scope, scope: &'s mut Scope,
this_ptr: &'s mut Option<&mut Dynamic>, this_ptr: &'s mut Dynamic,
expr: &Expr, expr: &Expr,
) -> RhaiResultOf<(Target<'s>, Position)> { ) -> RhaiResultOf<(Target<'s>, Position)> {
// Make sure that the pointer indirection is taken only when absolutely necessary. // Make sure that the pointer indirection is taken only when absolutely necessary.
@ -148,10 +148,11 @@ impl Engine {
let (index, var_pos) = match expr { let (index, var_pos) = match expr {
// Check if the variable is `this` // Check if the variable is `this`
Expr::Variable(v, None, pos) if v.0.is_none() && v.3 == KEYWORD_THIS => { Expr::Variable(v, None, pos) if v.0.is_none() && v.3 == KEYWORD_THIS => {
return this_ptr.as_mut().map_or_else( return if this_ptr.is_null() {
|| Err(ERR::ErrorUnboundThis(*pos).into()), Err(ERR::ErrorUnboundThis(*pos).into())
|val| Ok(((*val).into(), *pos)), } else {
) Ok((this_ptr.into(), *pos))
};
} }
_ if global.always_search_scope => (0, expr.start_position()), _ if global.always_search_scope => (0, expr.start_position()),
Expr::Variable(.., Some(i), pos) => (i.get() as usize, *pos), Expr::Variable(.., Some(i), pos) => (i.get() as usize, *pos),
@ -224,7 +225,7 @@ impl Engine {
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Dynamic,
expr: &Expr, expr: &Expr,
) -> RhaiResult { ) -> RhaiResult {
// Coded this way for better branch prediction. // Coded this way for better branch prediction.
@ -256,10 +257,11 @@ impl Engine {
self.track_operation(global, expr.position())?; self.track_operation(global, expr.position())?;
return if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS { return if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS {
this_ptr if this_ptr.is_null() {
.as_deref() ERR::ErrorUnboundThis(*var_pos).into()
.cloned() } else {
.ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()) Ok(this_ptr.clone())
}
} else { } else {
self.search_namespace(global, caches, lib, level, scope, this_ptr, expr) self.search_namespace(global, caches, lib, level, scope, this_ptr, expr)
.map(|(val, ..)| val.take_or_clone()) .map(|(val, ..)| val.take_or_clone())

View File

@ -29,7 +29,7 @@ impl Engine {
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Dynamic,
statements: &[Stmt], statements: &[Stmt],
restore_orig_state: bool, restore_orig_state: bool,
) -> RhaiResult { ) -> RhaiResult {
@ -203,7 +203,7 @@ impl Engine {
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Dynamic,
stmt: &Stmt, stmt: &Stmt,
rewind_scope: bool, rewind_scope: bool,
) -> RhaiResult { ) -> RhaiResult {
@ -912,8 +912,10 @@ impl Engine {
scope: &mut Scope, scope: &mut Scope,
statements: &[Stmt], statements: &[Stmt],
) -> RhaiResult { ) -> RhaiResult {
let mut this = Dynamic::NULL;
self.eval_stmt_block( self.eval_stmt_block(
global, caches, lib, level, scope, &mut None, statements, false, global, caches, lib, level, scope, &mut this, statements, false,
) )
.or_else(|err| match *err { .or_else(|err| match *err {
ERR::Return(out, ..) => Ok(out), ERR::Return(out, ..) => Ok(out),

View File

@ -11,8 +11,8 @@ use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState};
use crate::tokenizer::{is_valid_function_name, Token}; use crate::tokenizer::{is_valid_function_name, Token};
use crate::types::RestoreOnDrop; use crate::types::RestoreOnDrop;
use crate::{ use crate::{
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, SharedModule, calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString,
OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, ERR, OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use hashbrown::hash_map::Entry; use hashbrown::hash_map::Entry;
@ -407,6 +407,7 @@ impl Engine {
}; };
if trigger { if trigger {
let scope = &mut &mut Scope::new(); let scope = &mut &mut Scope::new();
let mut this = Dynamic::NULL;
let node = crate::ast::Stmt::Noop(pos); let node = crate::ast::Stmt::Noop(pos);
let node = (&node).into(); let node = (&node).into();
let event = match _result { let event = match _result {
@ -415,7 +416,7 @@ impl Engine {
}; };
if let Err(err) = self if let Err(err) = self
.run_debugger_raw(global, caches, lib, level, scope, &mut None, node, event) .run_debugger_raw(global, caches, lib, level, scope, &mut this, node, event)
{ {
_result = Err(err); _result = Err(err);
} }
@ -646,16 +647,7 @@ impl Engine {
let (first_arg, rest_args) = args.split_first_mut().unwrap(); let (first_arg, rest_args) = args.split_first_mut().unwrap();
self.call_script_fn( self.call_script_fn(
global, global, caches, lib, level, scope, first_arg, func, rest_args, true, pos,
caches,
lib,
level,
scope,
&mut Some(*first_arg),
func,
rest_args,
true,
pos,
) )
} else { } else {
// Normal call of script function // Normal call of script function
@ -672,8 +664,10 @@ impl Engine {
backup.restore_first_arg(a) backup.restore_first_arg(a)
}); });
let mut this = Dynamic::NULL;
self.call_script_fn( self.call_script_fn(
global, caches, lib, level, scope, &mut None, func, args, true, pos, global, caches, lib, level, scope, &mut this, func, args, true, pos,
) )
} }
.map(|r| (r, false)); .map(|r| (r, false));
@ -697,7 +691,7 @@ impl Engine {
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Dynamic,
arg_expr: &Expr, arg_expr: &Expr,
) -> RhaiResultOf<(Dynamic, Position)> { ) -> RhaiResultOf<(Dynamic, Position)> {
// Literal values // Literal values
@ -956,7 +950,7 @@ impl Engine {
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Dynamic,
fn_name: &str, fn_name: &str,
op_token: Option<&Token>, op_token: Option<&Token>,
first_arg: Option<&Expr>, first_arg: Option<&Expr>,
@ -1247,7 +1241,7 @@ impl Engine {
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Dynamic,
namespace: &crate::ast::Namespace, namespace: &crate::ast::Namespace,
fn_name: &str, fn_name: &str,
args_expr: &[Expr], args_expr: &[Expr],
@ -1376,12 +1370,13 @@ impl Engine {
Some(f) if f.is_script() => { Some(f) if f.is_script() => {
let fn_def = f.get_script_fn_def().expect("script-defined function"); let fn_def = f.get_script_fn_def().expect("script-defined function");
let new_scope = &mut Scope::new(); let new_scope = &mut Scope::new();
let mut this = Dynamic::NULL;
let orig_source = mem::replace(&mut global.source, module.id_raw().cloned()); let orig_source = mem::replace(&mut global.source, module.id_raw().cloned());
let global = &mut *RestoreOnDrop::lock(global, move |g| g.source = orig_source); let global = &mut *RestoreOnDrop::lock(global, move |g| g.source = orig_source);
self.call_script_fn( self.call_script_fn(
global, caches, lib, level, new_scope, &mut None, fn_def, &mut args, true, pos, global, caches, lib, level, new_scope, &mut this, fn_def, &mut args, true, pos,
) )
} }
@ -1474,7 +1469,7 @@ impl Engine {
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Dynamic,
expr: &FnCallExpr, expr: &FnCallExpr,
pos: Position, pos: Position,
) -> RhaiResult { ) -> RhaiResult {

View File

@ -29,7 +29,7 @@ impl Engine {
lib: &[SharedModule], lib: &[SharedModule],
level: usize, level: usize,
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Dynamic,
fn_def: &ScriptFnDef, fn_def: &ScriptFnDef,
args: &mut FnCallArgs, args: &mut FnCallArgs,
rewind_scope: bool, rewind_scope: bool,

View File

@ -50,7 +50,7 @@ struct OptimizerState<'a> {
/// Has the [`AST`] been changed during this pass? /// Has the [`AST`] been changed during this pass?
changed: bool, changed: bool,
/// Collection of constants to use for eager function evaluations. /// Collection of constants to use for eager function evaluations.
variables: StaticVec<(Identifier, AccessMode, Option<Dynamic>)>, variables: StaticVec<(Identifier, AccessMode, Dynamic)>,
/// Activate constants propagation? /// Activate constants propagation?
propagate_constants: bool, propagate_constants: bool,
/// An [`Engine`] instance for eager function evaluation. /// An [`Engine`] instance for eager function evaluation.
@ -108,12 +108,7 @@ impl<'a> OptimizerState<'a> {
} }
/// Add a new variable to the list. /// Add a new variable to the list.
#[inline(always)] #[inline(always)]
pub fn push_var( pub fn push_var(&mut self, name: impl Into<Identifier>, access: AccessMode, value: Dynamic) {
&mut self,
name: impl Into<Identifier>,
access: AccessMode,
value: Option<Dynamic>,
) {
self.variables.push((name.into(), access, value)); self.variables.push((name.into(), access, value));
} }
/// Look up a constant from the list. /// Look up a constant from the list.
@ -127,7 +122,8 @@ impl<'a> OptimizerState<'a> {
if n == name { if n == name {
return match access { return match access {
AccessMode::ReadWrite => None, AccessMode::ReadWrite => None,
AccessMode::ReadOnly => value.as_ref(), AccessMode::ReadOnly if value.is_null() => None,
AccessMode::ReadOnly => Some(value),
}; };
} }
} }
@ -141,7 +137,7 @@ impl<'a> OptimizerState<'a> {
fn_name: &str, fn_name: &str,
op_token: Option<&Token>, op_token: Option<&Token>,
arg_values: &mut [Dynamic], arg_values: &mut [Dynamic],
) -> Option<Dynamic> { ) -> Dynamic {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let lib = self.lib; let lib = self.lib;
#[cfg(feature = "no_function")] #[cfg(feature = "no_function")]
@ -160,8 +156,7 @@ impl<'a> OptimizerState<'a> {
false, false,
Position::NONE, Position::NONE,
) )
.ok() .map_or(Dynamic::NULL, |(v, ..)| v)
.map(|(v, ..)| v)
} }
} }
@ -271,13 +266,13 @@ fn optimize_stmt_block(
state.push_var( state.push_var(
x.0.as_str(), x.0.as_str(),
AccessMode::ReadOnly, AccessMode::ReadOnly,
x.1.get_literal_value(), x.1.get_literal_value().unwrap_or(Dynamic::NULL),
); );
} }
} else { } else {
// Add variables into the state // Add variables into the state
optimize_expr(&mut x.1, state, false); optimize_expr(&mut x.1, state, false);
state.push_var(x.0.as_str(), AccessMode::ReadWrite, None); state.push_var(x.0.as_str(), AccessMode::ReadWrite, Dynamic::NULL);
} }
} }
// Optimize the statement // Optimize the statement
@ -1197,13 +1192,13 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
let arg_values = &mut x.args.iter().map(Expr::get_literal_value).collect::<Option<StaticVec<_>>>().unwrap(); let arg_values = &mut x.args.iter().map(Expr::get_literal_value).collect::<Option<StaticVec<_>>>().unwrap();
let result = match x.name.as_str() { let result = match x.name.as_str() {
KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()), KEYWORD_TYPE_OF if arg_values.len() == 1 => state.engine.map_type_name(arg_values[0].type_name()).into(),
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Some(Dynamic::FALSE), crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Dynamic::FALSE,
_ => state.call_fn_with_constant_arguments(&x.name, x.op_token.as_ref(), arg_values) _ => state.call_fn_with_constant_arguments(&x.name, x.op_token.as_ref(), arg_values)
}; };
if let Some(result) = result { if !result.is_null() {
state.set_dirty(); state.set_dirty();
*expr = Expr::from_dynamic(result, *pos); *expr = Expr::from_dynamic(result, *pos);
return; return;
@ -1289,15 +1284,15 @@ fn optimize_top_level(
.rev() .rev()
.flat_map(|m| m.iter_var()) .flat_map(|m| m.iter_var())
{ {
state.push_var(name, AccessMode::ReadOnly, Some(value.clone())); state.push_var(name, AccessMode::ReadOnly, value.clone());
} }
// Add constants and variables from the scope // Add constants and variables from the scope
for (name, constant, value) in scope.iter() { for (name, constant, value) in scope.iter() {
if constant { if constant {
state.push_var(name, AccessMode::ReadOnly, Some(value)); state.push_var(name, AccessMode::ReadOnly, value);
} else { } else {
state.push_var(name, AccessMode::ReadWrite, None); state.push_var(name, AccessMode::ReadWrite, Dynamic::NULL);
} }
} }

View File

@ -2907,7 +2907,7 @@ impl Engine {
will_shadow, will_shadow,
}; };
let caches = &mut Caches::new(); let caches = &mut Caches::new();
let this_ptr = &mut None; let mut this = Dynamic::NULL;
let context = EvalContext::new( let context = EvalContext::new(
self, self,
@ -2916,7 +2916,7 @@ impl Engine {
&[], &[],
level, level,
&mut state.stack, &mut state.stack,
this_ptr, &mut this,
); );
match filter(false, info, context) { match filter(false, info, context) {

View File

@ -125,6 +125,8 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
match self.0 .0 { match self.0 .0 {
Union::Null => unreachable!(),
Union::Unit(..) => self.deserialize_unit(visitor), Union::Unit(..) => self.deserialize_unit(visitor),
Union::Bool(..) => self.deserialize_bool(visitor), Union::Bool(..) => self.deserialize_bool(visitor),
Union::Str(..) => self.deserialize_str(visitor), Union::Str(..) => self.deserialize_str(visitor),

View File

@ -15,6 +15,8 @@ use crate::types::dynamic::Variant;
impl Serialize for Dynamic { impl Serialize for Dynamic {
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> { fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
match self.0 { match self.0 {
Union::Null => unreachable!(),
Union::Unit(..) => ser.serialize_unit(), Union::Unit(..) => ser.serialize_unit(),
Union::Bool(x, ..) => ser.serialize_bool(x), Union::Bool(x, ..) => ser.serialize_bool(x),
Union::Str(ref s, ..) => ser.serialize_str(s.as_str()), Union::Str(ref s, ..) => ser.serialize_str(s.as_str()),

View File

@ -55,6 +55,9 @@ pub struct Dynamic(pub(crate) Union);
/// ///
/// Most variants are boxed to reduce the size. /// Most variants are boxed to reduce the size.
pub enum Union { pub enum Union {
/// An error value which should not exist.
Null,
/// The Unit value - (). /// The Unit value - ().
Unit((), Tag, AccessMode), Unit((), Tag, AccessMode),
/// A boolean value. /// A boolean value.
@ -178,6 +181,8 @@ impl Dynamic {
#[must_use] #[must_use]
pub const fn tag(&self) -> Tag { pub const fn tag(&self) -> Tag {
match self.0 { match self.0 {
Union::Null => unreachable!(),
Union::Unit(_, tag, _) Union::Unit(_, tag, _)
| Union::Bool(_, tag, _) | Union::Bool(_, tag, _)
| Union::Str(_, tag, _) | Union::Str(_, tag, _)
@ -203,6 +208,8 @@ impl Dynamic {
/// Attach arbitrary data to this [`Dynamic`]. /// Attach arbitrary data to this [`Dynamic`].
pub fn set_tag(&mut self, value: Tag) -> &mut Self { pub fn set_tag(&mut self, value: Tag) -> &mut Self {
match self.0 { match self.0 {
Union::Null => unreachable!(),
Union::Unit(_, ref mut tag, _) Union::Unit(_, ref mut tag, _)
| Union::Bool(_, ref mut tag, _) | Union::Bool(_, ref mut tag, _)
| Union::Str(_, ref mut tag, _) | Union::Str(_, ref mut tag, _)
@ -226,6 +233,12 @@ impl Dynamic {
} }
self self
} }
/// Is this [`Dynamic`] null?
#[inline(always)]
#[must_use]
pub(crate) const fn is_null(&self) -> bool {
matches!(self.0, Union::Null)
}
/// Does this [`Dynamic`] hold a variant data type instead of one of the supported system /// Does this [`Dynamic`] hold a variant data type instead of one of the supported system
/// primitive types? /// primitive types?
#[inline(always)] #[inline(always)]
@ -307,6 +320,8 @@ impl Dynamic {
#[must_use] #[must_use]
pub fn type_id(&self) -> TypeId { pub fn type_id(&self) -> TypeId {
match self.0 { match self.0 {
Union::Null => unreachable!(),
Union::Unit(..) => TypeId::of::<()>(), Union::Unit(..) => TypeId::of::<()>(),
Union::Bool(..) => TypeId::of::<bool>(), Union::Bool(..) => TypeId::of::<bool>(),
Union::Str(..) => TypeId::of::<ImmutableString>(), Union::Str(..) => TypeId::of::<ImmutableString>(),
@ -341,6 +356,8 @@ impl Dynamic {
#[must_use] #[must_use]
pub fn type_name(&self) -> &'static str { pub fn type_name(&self) -> &'static str {
match self.0 { match self.0 {
Union::Null => unreachable!(),
Union::Unit(..) => "()", Union::Unit(..) => "()",
Union::Bool(..) => "bool", Union::Bool(..) => "bool",
Union::Str(..) => "string", Union::Str(..) => "string",
@ -385,6 +402,8 @@ impl Hash for Dynamic {
mem::discriminant(&self.0).hash(state); mem::discriminant(&self.0).hash(state);
match self.0 { match self.0 {
Union::Null => unreachable!(),
Union::Unit(..) => (), Union::Unit(..) => (),
Union::Bool(ref b, ..) => b.hash(state), Union::Bool(ref b, ..) => b.hash(state),
Union::Str(ref s, ..) => s.hash(state), Union::Str(ref s, ..) => s.hash(state),
@ -416,6 +435,8 @@ impl Hash for Dynamic {
impl fmt::Display for Dynamic { impl fmt::Display for Dynamic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 { match self.0 {
Union::Null => unreachable!(),
Union::Unit(..) => Ok(()), Union::Unit(..) => Ok(()),
Union::Bool(ref v, ..) => fmt::Display::fmt(v, f), Union::Bool(ref v, ..) => fmt::Display::fmt(v, f),
Union::Str(ref v, ..) => fmt::Display::fmt(v, f), Union::Str(ref v, ..) => fmt::Display::fmt(v, f),
@ -509,6 +530,8 @@ impl fmt::Debug for Dynamic {
#[inline(never)] #[inline(never)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 { match self.0 {
Union::Null => unreachable!(),
Union::Unit(ref v, ..) => fmt::Debug::fmt(v, f), Union::Unit(ref v, ..) => fmt::Debug::fmt(v, f),
Union::Bool(ref v, ..) => fmt::Debug::fmt(v, f), Union::Bool(ref v, ..) => fmt::Debug::fmt(v, f),
Union::Str(ref v, ..) => fmt::Debug::fmt(v, f), Union::Str(ref v, ..) => fmt::Debug::fmt(v, f),
@ -619,6 +642,8 @@ impl Clone for Dynamic {
/// The cloned copy is marked read-write even if the original is read-only. /// The cloned copy is marked read-write even if the original is read-only.
fn clone(&self) -> Self { fn clone(&self) -> Self {
match self.0 { match self.0 {
Union::Null => unreachable!(),
Union::Unit(v, tag, ..) => Self(Union::Unit(v, tag, ReadWrite)), Union::Unit(v, tag, ..) => Self(Union::Unit(v, tag, ReadWrite)),
Union::Bool(v, tag, ..) => Self(Union::Bool(v, tag, ReadWrite)), Union::Bool(v, tag, ..) => Self(Union::Bool(v, tag, ReadWrite)),
Union::Str(ref v, tag, ..) => Self(Union::Str(v.clone(), tag, ReadWrite)), Union::Str(ref v, tag, ..) => Self(Union::Str(v.clone(), tag, ReadWrite)),
@ -666,6 +691,9 @@ use std::f32::consts as FloatConstants;
use std::f64::consts as FloatConstants; use std::f64::consts as FloatConstants;
impl Dynamic { impl Dynamic {
/// A [`Dynamic`] containing a `null`.
pub(crate) const NULL: Self = Self(Union::Null);
/// A [`Dynamic`] containing a `()`. /// A [`Dynamic`] containing a `()`.
pub const UNIT: Self = Self(Union::Unit((), DEFAULT_TAG_VALUE, ReadWrite)); pub const UNIT: Self = Self(Union::Unit((), DEFAULT_TAG_VALUE, ReadWrite));
/// A [`Dynamic`] containing a `true`. /// A [`Dynamic`] containing a `true`.
@ -888,6 +916,8 @@ impl Dynamic {
#[must_use] #[must_use]
pub(crate) const fn access_mode(&self) -> AccessMode { pub(crate) const fn access_mode(&self) -> AccessMode {
match self.0 { match self.0 {
Union::Null => unreachable!(),
Union::Unit(.., access) Union::Unit(.., access)
| Union::Bool(.., access) | Union::Bool(.., access)
| Union::Str(.., access) | Union::Str(.., access)
@ -913,6 +943,8 @@ impl Dynamic {
/// Set the [`AccessMode`] for this [`Dynamic`]. /// Set the [`AccessMode`] for this [`Dynamic`].
pub(crate) fn set_access_mode(&mut self, typ: AccessMode) -> &mut Self { pub(crate) fn set_access_mode(&mut self, typ: AccessMode) -> &mut Self {
match self.0 { match self.0 {
Union::Null => unreachable!(),
Union::Unit(.., ref mut access) Union::Unit(.., ref mut access)
| Union::Bool(.., ref mut access) | Union::Bool(.., ref mut access)
| Union::Str(.., ref mut access) | Union::Str(.., ref mut access)
@ -1107,6 +1139,7 @@ impl Dynamic {
let _access = self.access_mode(); let _access = self.access_mode();
match self.0 { match self.0 {
Union::Null => unreachable!(),
Union::Shared(..) => self, Union::Shared(..) => self,
_ => Self(Union::Shared( _ => Self(Union::Shared(
crate::Locked::new(self).into(), crate::Locked::new(self).into(),
@ -1151,6 +1184,8 @@ impl Dynamic {
reify!(self, |v: T| return Some(v)); reify!(self, |v: T| return Some(v));
match self.0 { match self.0 {
Union::Null => unreachable!(),
Union::Int(v, ..) => reify!(v => Option<T>), Union::Int(v, ..) => reify!(v => Option<T>),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Union::Float(v, ..) => reify!(*v => Option<T>), Union::Float(v, ..) => reify!(*v => Option<T>),
@ -1485,6 +1520,7 @@ impl Dynamic {
} }
match self.0 { match self.0 {
Union::Null => unreachable!(),
Union::Variant(ref v, ..) => (***v).as_any().downcast_ref::<T>(), Union::Variant(ref v, ..) => (***v).as_any().downcast_ref::<T>(),
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(..) => None, Union::Shared(..) => None,
@ -1583,6 +1619,7 @@ impl Dynamic {
} }
match self.0 { match self.0 {
Union::Null => unreachable!(),
Union::Variant(ref mut v, ..) => (***v).as_any_mut().downcast_mut::<T>(), Union::Variant(ref mut v, ..) => (***v).as_any_mut().downcast_mut::<T>(),
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(..) => None, Union::Shared(..) => None,

0
unreachable!() Normal file
View File