Check if function calls cannot be scripted.

This commit is contained in:
Stephen Chung 2022-10-14 18:31:40 +08:00
parent ea63c66cf0
commit ac05f0a0a8
6 changed files with 142 additions and 102 deletions

View File

@ -181,7 +181,7 @@ impl FnCallHashes {
/// _(internals)_ A function call. /// _(internals)_ A function call.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[derive(Clone, Default, Hash)] #[derive(Clone, Hash)]
pub struct FnCallExpr { pub struct FnCallExpr {
/// Namespace of the function, if any. /// Namespace of the function, if any.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
@ -196,6 +196,9 @@ pub struct FnCallExpr {
pub capture_parent_scope: bool, pub capture_parent_scope: bool,
/// Is this function call a native operator? /// Is this function call a native operator?
pub operator_token: Option<Token>, pub operator_token: Option<Token>,
/// Can this function call be a scripted function?
#[cfg(not(feature = "no_function"))]
pub can_be_script: bool,
/// [Position] of the function name. /// [Position] of the function name.
pub pos: Position, pub pos: Position,
} }
@ -215,6 +218,10 @@ impl fmt::Debug for FnCallExpr {
if let Some(ref token) = self.operator_token { if let Some(ref token) = self.operator_token {
ff.field("operator_token", token); ff.field("operator_token", token);
} }
#[cfg(not(feature = "no_function"))]
if self.can_be_script {
ff.field("can_be_script", &self.can_be_script);
}
ff.field("hash", &self.hashes) ff.field("hash", &self.hashes)
.field("name", &self.name) .field("name", &self.name)
.field("args", &self.args); .field("args", &self.args);
@ -684,6 +691,8 @@ impl Expr {
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(), args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
capture_parent_scope: false, capture_parent_scope: false,
operator_token: None, operator_token: None,
#[cfg(not(feature = "no_function"))]
can_be_script: true,
pos, pos,
} }
.into(), .into(),

View File

@ -223,9 +223,16 @@ impl Engine {
hashes, hashes,
args, args,
operator_token, operator_token,
#[cfg(not(feature = "no_function"))]
can_be_script,
.. ..
} = expr; } = expr;
#[cfg(not(feature = "no_function"))]
let native = !can_be_script;
#[cfg(feature = "no_function")]
let native = true;
// Short-circuit native binary operator call if under Fast Operators mode // Short-circuit native binary operator call if under Fast Operators mode
if operator_token.is_some() && self.fast_operators() && args.len() == 2 { if operator_token.is_some() && self.fast_operators() && args.len() == 2 {
let mut lhs = self let mut lhs = self
@ -251,7 +258,8 @@ impl Engine {
return self return self
.exec_fn_call( .exec_fn_call(
None, global, caches, lib, name, *hashes, operands, false, false, pos, level, None, global, caches, lib, name, native, *hashes, operands, false, false, pos,
level,
) )
.map(|(v, ..)| v); .map(|(v, ..)| v);
} }
@ -280,6 +288,7 @@ impl Engine {
lib, lib,
this_ptr, this_ptr,
name, name,
native,
first_arg, first_arg,
args, args,
*hashes, *hashes,

View File

@ -578,6 +578,7 @@ impl Engine {
caches: &mut Caches, caches: &mut Caches,
lib: &[&Module], lib: &[&Module],
fn_name: &str, fn_name: &str,
_native_only: bool,
hashes: FnCallHashes, hashes: FnCallHashes,
args: &mut FnCallArgs, args: &mut FnCallArgs,
is_ref_mut: bool, is_ref_mut: bool,
@ -644,89 +645,90 @@ impl Engine {
let level = level + 1; let level = level + 1;
// Script-defined function call?
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let local_entry = &mut None; if !_native_only {
// Script-defined function call?
let local_entry = &mut None;
#[cfg(not(feature = "no_function"))] if let Some(FnResolutionCacheEntry { func, ref source }) = self
if let Some(FnResolutionCacheEntry { func, ref source }) = self .resolve_fn(
.resolve_fn(
global,
caches,
local_entry,
lib,
fn_name,
hashes.script,
None,
false,
None,
)
.cloned()
{
// Script function call
assert!(func.is_script());
let func = func.get_script_fn_def().expect("script-defined function");
if func.body.is_empty() {
return Ok((Dynamic::UNIT, false));
}
let mut empty_scope;
let scope = match _scope {
Some(scope) => scope,
None => {
empty_scope = Scope::new();
&mut empty_scope
}
};
let orig_source = mem::replace(
&mut global.source,
source
.as_ref()
.map_or(crate::Identifier::new_const(), |s| (**s).clone()),
);
let result = if _is_method_call {
// Method call of script function - map first argument to `this`
let (first_arg, rest_args) = args.split_first_mut().unwrap();
self.call_script_fn(
scope,
global, global,
caches, caches,
local_entry,
lib, lib,
&mut Some(*first_arg), fn_name,
func, hashes.script,
rest_args, None,
true, false,
pos, None,
level,
) )
} else { .cloned()
// Normal call of script function {
let mut backup = ArgBackup::new(); // Script function call
assert!(func.is_script());
// The first argument is a reference? let func = func.get_script_fn_def().expect("script-defined function");
if is_ref_mut && !args.is_empty() {
backup.change_first_arg_to_copy(args); if func.body.is_empty() {
return Ok((Dynamic::UNIT, false));
} }
let result = self.call_script_fn( let mut empty_scope;
scope, global, caches, lib, &mut None, func, args, true, pos, level, let scope = match _scope {
Some(scope) => scope,
None => {
empty_scope = Scope::new();
&mut empty_scope
}
};
let orig_source = mem::replace(
&mut global.source,
source
.as_ref()
.map_or(crate::Identifier::new_const(), |s| (**s).clone()),
); );
// Restore the original reference let result = if _is_method_call {
backup.restore_first_arg(args); // Method call of script function - map first argument to `this`
let (first_arg, rest_args) = args.split_first_mut().unwrap();
result self.call_script_fn(
}; scope,
global,
caches,
lib,
&mut Some(*first_arg),
func,
rest_args,
true,
pos,
level,
)
} else {
// Normal call of script function
let mut backup = ArgBackup::new();
// Restore the original source // The first argument is a reference?
global.source = orig_source; if is_ref_mut && !args.is_empty() {
backup.change_first_arg_to_copy(args);
}
return Ok((result?, false)); let result = self.call_script_fn(
scope, global, caches, lib, &mut None, func, args, true, pos, level,
);
// Restore the original reference
backup.restore_first_arg(args);
result
};
// Restore the original source
global.source = orig_source;
return Ok((result?, false));
}
} }
// Native function call // Native function call
@ -836,6 +838,7 @@ impl Engine {
caches, caches,
lib, lib,
fn_name, fn_name,
false,
new_hash, new_hash,
&mut args, &mut args,
false, false,
@ -881,6 +884,7 @@ impl Engine {
caches, caches,
lib, lib,
fn_name, fn_name,
false,
new_hash, new_hash,
&mut args, &mut args,
is_ref_mut, is_ref_mut,
@ -968,6 +972,7 @@ impl Engine {
caches, caches,
lib, lib,
fn_name, fn_name,
false,
hash, hash,
&mut args, &mut args,
is_ref_mut, is_ref_mut,
@ -995,6 +1000,7 @@ impl Engine {
lib: &[&Module], lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
fn_name: &str, fn_name: &str,
native_only: bool,
first_arg: Option<&Expr>, first_arg: Option<&Expr>,
args_expr: &[Expr], args_expr: &[Expr],
hashes: FnCallHashes, hashes: FnCallHashes,
@ -1003,6 +1009,7 @@ impl Engine {
pos: Position, pos: Position,
level: usize, level: usize,
) -> RhaiResult { ) -> RhaiResult {
let native = native_only;
let mut first_arg = first_arg; let mut first_arg = first_arg;
let mut a_expr = args_expr; let mut a_expr = args_expr;
let mut total_args = if first_arg.is_some() { 1 } else { 0 } + a_expr.len(); let mut total_args = if first_arg.is_some() { 1 } else { 0 } + a_expr.len();
@ -1199,8 +1206,8 @@ impl Engine {
return self return self
.exec_fn_call( .exec_fn_call(
scope, global, caches, lib, name, hashes, &mut args, is_ref_mut, false, pos, scope, global, caches, lib, name, native, hashes, &mut args, is_ref_mut, false,
level, pos, level,
) )
.map(|(v, ..)| v); .map(|(v, ..)| v);
} }
@ -1262,7 +1269,8 @@ impl Engine {
} }
self.exec_fn_call( self.exec_fn_call(
None, global, caches, lib, name, hashes, &mut args, is_ref_mut, false, pos, level, None, global, caches, lib, name, native, hashes, &mut args, is_ref_mut, false, pos,
level,
) )
.map(|(v, ..)| v) .map(|(v, ..)| v)
} }

View File

@ -422,6 +422,7 @@ impl<'a> NativeCallContext<'a> {
caches, caches,
self.lib, self.lib,
fn_name, fn_name,
false,
hash, hash,
args, args,
is_ref_mut, is_ref_mut,

View File

@ -897,24 +897,17 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
Stmt::Expr(expr) => { Stmt::Expr(expr) => {
optimize_expr(expr, state, false); optimize_expr(expr, state, false);
match &mut **expr { if matches!(**expr, Expr::FnCall(..) | Expr::Stmt(..)) {
// func(...) state.set_dirty();
Expr::FnCall(x, pos) => { *stmt = match *mem::take(expr) {
state.set_dirty(); // func(...);
*stmt = Stmt::FnCall(mem::take(x), *pos); Expr::FnCall(x, pos) => Stmt::FnCall(x, pos),
} // {};
// {...}; Expr::Stmt(x) if x.is_empty() => Stmt::Noop(x.position()),
Expr::Stmt(x) => { // {...};
if x.is_empty() { Expr::Stmt(x) => (*x).into(),
state.set_dirty(); _ => unreachable!(),
*stmt = Stmt::Noop(x.position()); };
} else {
state.set_dirty();
*stmt = mem::take(&mut **x).into();
}
}
// expr;
_ => (),
} }
} }

View File

@ -193,7 +193,6 @@ impl<'e> ParseState<'e> {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let is_func_name = _lib.values().any(|f| f.name == name); let is_func_name = _lib.values().any(|f| f.name == name);
#[cfg(feature = "no_function")] #[cfg(feature = "no_function")]
let is_func_name = false; let is_func_name = false;
@ -600,7 +599,9 @@ impl Engine {
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
let hash = calc_fn_hash(None, &id, 0); let hash = calc_fn_hash(None, &id, 0);
let hashes = if is_valid_function_name(&id) { let is_valid_function_name = is_valid_function_name(&id);
let hashes = if is_valid_function_name {
hash.into() hash.into()
} else { } else {
FnCallHashes::from_native(hash) FnCallHashes::from_native(hash)
@ -612,6 +613,8 @@ impl Engine {
name: state.get_interned_string(id), name: state.get_interned_string(id),
capture_parent_scope, capture_parent_scope,
operator_token: None, operator_token: None,
#[cfg(not(feature = "no_function"))]
can_be_script: is_valid_function_name,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
namespace, namespace,
hashes, hashes,
@ -668,7 +671,9 @@ impl Engine {
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
let hash = calc_fn_hash(None, &id, args.len()); let hash = calc_fn_hash(None, &id, args.len());
let hashes = if is_valid_function_name(&id) { let is_valid_function_name = is_valid_function_name(&id);
let hashes = if is_valid_function_name {
hash.into() hash.into()
} else { } else {
FnCallHashes::from_native(hash) FnCallHashes::from_native(hash)
@ -680,6 +685,8 @@ impl Engine {
name: state.get_interned_string(id), name: state.get_interned_string(id),
capture_parent_scope, capture_parent_scope,
operator_token: None, operator_token: None,
#[cfg(not(feature = "no_function"))]
can_be_script: is_valid_function_name,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
namespace, namespace,
hashes, hashes,
@ -1912,12 +1919,14 @@ impl Engine {
args.shrink_to_fit(); args.shrink_to_fit();
Ok(FnCallExpr { Ok(FnCallExpr {
namespace: Default::default(),
name: state.get_interned_string("-"), name: state.get_interned_string("-"),
hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)), hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)),
args, args,
pos, pos,
operator_token: Some(token), operator_token: Some(token),
..Default::default() capture_parent_scope: false,
can_be_script: false,
} }
.into_fn_call_expr(pos)) .into_fn_call_expr(pos))
} }
@ -1940,12 +1949,14 @@ impl Engine {
args.shrink_to_fit(); args.shrink_to_fit();
Ok(FnCallExpr { Ok(FnCallExpr {
namespace: Default::default(),
name: state.get_interned_string("+"), name: state.get_interned_string("+"),
hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)), hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)),
args, args,
pos, pos,
operator_token: Some(token), operator_token: Some(token),
..Default::default() capture_parent_scope: false,
can_be_script: false,
} }
.into_fn_call_expr(pos)) .into_fn_call_expr(pos))
} }
@ -1961,12 +1972,14 @@ impl Engine {
args.shrink_to_fit(); args.shrink_to_fit();
Ok(FnCallExpr { Ok(FnCallExpr {
namespace: Default::default(),
name: state.get_interned_string("!"), name: state.get_interned_string("!"),
hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)), hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)),
args, args,
pos, pos,
operator_token: Some(token), operator_token: Some(token),
..Default::default() capture_parent_scope: false,
can_be_script: false,
} }
.into_fn_call_expr(pos)) .into_fn_call_expr(pos))
} }
@ -2335,18 +2348,22 @@ impl Engine {
let op = op_token.syntax(); let op = op_token.syntax();
let hash = calc_fn_hash(None, &op, 2); let hash = calc_fn_hash(None, &op, 2);
let operator_token = if is_valid_function_name(&op) { let is_function = is_valid_function_name(&op);
let operator_token = if is_function {
None None
} else { } else {
Some(op_token.clone()) Some(op_token.clone())
}; };
let op_base = FnCallExpr { let op_base = FnCallExpr {
namespace: Default::default(),
name: state.get_interned_string(op.as_ref()), name: state.get_interned_string(op.as_ref()),
hashes: FnCallHashes::from_native(hash), hashes: FnCallHashes::from_native(hash),
args: StaticVec::new_const(),
pos, pos,
operator_token, operator_token,
..Default::default() capture_parent_scope: false,
can_be_script: is_function,
}; };
let mut args = StaticVec::new_const(); let mut args = StaticVec::new_const();
@ -2432,7 +2449,7 @@ impl Engine {
let pos = args[0].start_position(); let pos = args[0].start_position();
FnCallExpr { FnCallExpr {
hashes: if is_valid_function_name(&s) { hashes: if is_function {
hash.into() hash.into()
} else { } else {
FnCallHashes::from_native(hash) FnCallHashes::from_native(hash)
@ -3659,6 +3676,7 @@ impl Engine {
); );
let expr = FnCallExpr { let expr = FnCallExpr {
namespace: Default::default(),
name: state.get_interned_string(crate::engine::KEYWORD_FN_PTR_CURRY), name: state.get_interned_string(crate::engine::KEYWORD_FN_PTR_CURRY),
hashes: FnCallHashes::from_native(calc_fn_hash( hashes: FnCallHashes::from_native(calc_fn_hash(
None, None,
@ -3667,7 +3685,9 @@ impl Engine {
)), )),
args, args,
pos, pos,
..Default::default() operator_token: None,
capture_parent_scope: false,
can_be_script: false,
} }
.into_fn_call_expr(pos); .into_fn_call_expr(pos);