Change FnPtr to SmartString.

This commit is contained in:
Stephen Chung 2021-04-20 23:28:04 +08:00
parent 8f1bff2ded
commit ce384d8bb0
5 changed files with 37 additions and 28 deletions

View File

@ -1444,21 +1444,26 @@ impl FnCallHashes {
/// This type is volatile and may change. /// This type is volatile and may change.
#[derive(Clone, Default, Hash)] #[derive(Clone, Default, Hash)]
pub struct FnCallExpr { pub struct FnCallExpr {
/// Namespace of the function, if any.
pub namespace: Option<NamespaceRef>,
/// Pre-calculated hashes. /// Pre-calculated hashes.
pub hashes: FnCallHashes, pub hashes: FnCallHashes,
/// Does this function call capture the parent scope?
pub capture: bool,
/// List of function call argument expressions. /// List of function call argument expressions.
pub args: StaticVec<Expr>, pub args: StaticVec<Expr>,
/// List of function call arguments that are constants. /// List of function call arguments that are constants.
pub constant_args: smallvec::SmallVec<[(Dynamic, Position); 2]>, pub constant_args: smallvec::SmallVec<[(Dynamic, Position); 2]>,
/// Namespace of the function, if any. Boxed because it occurs rarely.
pub namespace: Option<NamespaceRef>,
/// Function name. /// Function name.
pub name: Identifier, pub name: Identifier,
/// Does this function call capture the parent scope?
pub capture: bool,
} }
impl FnCallExpr { impl FnCallExpr {
/// Does this function call contain a qualified namespace?
#[inline(always)]
pub fn is_qualified(&self) -> bool {
self.namespace.is_some()
}
/// Are there no arguments to this function call? /// Are there no arguments to this function call?
#[inline(always)] #[inline(always)]
pub fn is_args_empty(&self) -> bool { pub fn is_args_empty(&self) -> bool {
@ -1466,7 +1471,7 @@ impl FnCallExpr {
} }
/// Get the number of arguments to this function call. /// Get the number of arguments to this function call.
#[inline(always)] #[inline(always)]
pub fn num_args(&self) -> usize { pub fn args_count(&self) -> usize {
self.args.len() + self.constant_args.len() self.args.len() + self.constant_args.len()
} }
} }
@ -1609,7 +1614,7 @@ pub enum Expr {
/// [String][ImmutableString] constant. /// [String][ImmutableString] constant.
StringConstant(ImmutableString, Position), StringConstant(ImmutableString, Position),
/// [`FnPtr`] constant. /// [`FnPtr`] constant.
FnPointer(ImmutableString, Position), FnPointer(Box<Identifier>, Position),
/// An interpolated [string][ImmutableString]. /// An interpolated [string][ImmutableString].
InterpolatedString(Box<StaticVec<Expr>>), InterpolatedString(Box<StaticVec<Expr>>),
/// [ expr, ... ] /// [ expr, ... ]
@ -1760,7 +1765,9 @@ impl Expr {
Self::FloatConstant(x, _) => (*x).into(), Self::FloatConstant(x, _) => (*x).into(),
Self::CharConstant(x, _) => (*x).into(), Self::CharConstant(x, _) => (*x).into(),
Self::StringConstant(x, _) => x.clone().into(), Self::StringConstant(x, _) => x.clone().into(),
Self::FnPointer(x, _) => FnPtr::new_unchecked(x.clone(), Default::default()).into(), Self::FnPointer(x, _) => {
FnPtr::new_unchecked(x.as_ref().clone(), Default::default()).into()
}
Self::BoolConstant(x, _) => (*x).into(), Self::BoolConstant(x, _) => (*x).into(),
Self::Unit(_) => Dynamic::UNIT, Self::Unit(_) => Dynamic::UNIT,

View File

@ -1227,7 +1227,7 @@ impl Engine {
ChainType::Dot => { ChainType::Dot => {
match rhs { match rhs {
// xxx.fn_name(arg_expr_list) // xxx.fn_name(arg_expr_list)
Expr::FnCall(x, pos) if x.namespace.is_none() && new_val.is_none() => { Expr::FnCall(x, pos) if !x.is_qualified() && new_val.is_none() => {
let FnCallExpr { name, hashes, .. } = x.as_ref(); let FnCallExpr { name, hashes, .. } = x.as_ref();
let mut args = idx_val.as_fn_call_args(); let mut args = idx_val.as_fn_call_args();
self.make_method_call( self.make_method_call(
@ -1316,7 +1316,7 @@ impl Engine {
)? )?
} }
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
Expr::FnCall(x, pos) if x.namespace.is_none() => { Expr::FnCall(x, pos) if !x.is_qualified() => {
let FnCallExpr { name, hashes, .. } = x.as_ref(); let FnCallExpr { name, hashes, .. } = x.as_ref();
let mut args = idx_val.as_fn_call_args(); let mut args = idx_val.as_fn_call_args();
let (val, _) = self.make_method_call( let (val, _) = self.make_method_call(
@ -1394,7 +1394,7 @@ impl Engine {
Ok((result, may_be_changed)) Ok((result, may_be_changed))
} }
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
Expr::FnCall(f, pos) if f.namespace.is_none() => { Expr::FnCall(f, pos) if !f.is_qualified() => {
let FnCallExpr { name, hashes, .. } = f.as_ref(); let FnCallExpr { name, hashes, .. } = f.as_ref();
let mut args = idx_val.as_fn_call_args(); let mut args = idx_val.as_fn_call_args();
let (mut val, _) = self.make_method_call( let (mut val, _) = self.make_method_call(
@ -1508,7 +1508,7 @@ impl Engine {
self.inc_operations(state, expr.position())?; self.inc_operations(state, expr.position())?;
match expr { match expr {
Expr::FnCall(x, _) if parent_chain_type == ChainType::Dot && x.namespace.is_none() => { Expr::FnCall(x, _) if parent_chain_type == ChainType::Dot && !x.is_qualified() => {
let mut arg_positions: StaticVec<_> = Default::default(); let mut arg_positions: StaticVec<_> = Default::default();
let mut arg_values = x let mut arg_values = x
@ -1547,7 +1547,7 @@ impl Engine {
} }
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
Expr::FnCall(x, _) Expr::FnCall(x, _)
if parent_chain_type == ChainType::Dot && x.namespace.is_none() => if parent_chain_type == ChainType::Dot && !x.is_qualified() =>
{ {
let mut arg_positions: StaticVec<_> = Default::default(); let mut arg_positions: StaticVec<_> = Default::default();
@ -1749,7 +1749,9 @@ impl Engine {
Expr::FloatConstant(x, _) => Ok((*x).into()), Expr::FloatConstant(x, _) => Ok((*x).into()),
Expr::StringConstant(x, _) => Ok(x.clone().into()), Expr::StringConstant(x, _) => Ok(x.clone().into()),
Expr::CharConstant(x, _) => Ok((*x).into()), Expr::CharConstant(x, _) => Ok((*x).into()),
Expr::FnPointer(x, _) => Ok(FnPtr::new_unchecked(x.clone(), Default::default()).into()), Expr::FnPointer(x, _) => {
Ok(FnPtr::new_unchecked(x.as_ref().clone(), Default::default()).into())
}
Expr::Variable(None, var_pos, x) if x.0.is_none() && x.2 == KEYWORD_THIS => this_ptr Expr::Variable(None, var_pos, x) if x.0.is_none() && x.2 == KEYWORD_THIS => this_ptr
.as_deref() .as_deref()
@ -1829,7 +1831,7 @@ impl Engine {
} }
// Normal function call // Normal function call
Expr::FnCall(x, pos) if x.namespace.is_none() => { Expr::FnCall(x, pos) if !x.is_qualified() => {
let FnCallExpr { let FnCallExpr {
name, name,
capture, capture,
@ -1845,7 +1847,7 @@ impl Engine {
} }
// Namespace-qualified function call // Namespace-qualified function call
Expr::FnCall(x, pos) if x.namespace.is_some() => { Expr::FnCall(x, pos) if x.is_qualified() => {
let FnCallExpr { let FnCallExpr {
name, name,
namespace, namespace,

View File

@ -262,7 +262,7 @@ impl FnPtr {
} }
/// Create a new function pointer without checking its parameters. /// Create a new function pointer without checking its parameters.
#[inline(always)] #[inline(always)]
pub(crate) fn new_unchecked(name: impl Into<Identifier>, curry: StaticVec<Dynamic>) -> Self { pub(crate) fn new_unchecked(name: Identifier, curry: StaticVec<Dynamic>) -> Self {
Self(name.into(), curry) Self(name.into(), curry)
} }
/// Get the name of the function. /// Get the name of the function.

View File

@ -862,15 +862,15 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
} }
// Fn // Fn
Expr::FnCall(x, pos) Expr::FnCall(x, pos)
if x.namespace.is_none() // Non-qualified if !x.is_qualified() // Non-qualified
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations && state.optimization_level == OptimizationLevel::Simple // simple optimizations
&& x.num_args() == 1 && x.args_count() == 1
&& x.constant_args.len() == 1 && x.constant_args.len() == 1
&& x.constant_args[0].0.is::<ImmutableString>() && x.constant_args[0].0.is::<ImmutableString>()
&& x.name == KEYWORD_FN_PTR && x.name == KEYWORD_FN_PTR
=> { => {
state.set_dirty(); state.set_dirty();
*expr = Expr::FnPointer(mem::take(&mut x.constant_args[0].0).take_immutable_string().unwrap(), *pos); *expr = Expr::FnPointer(Box::new(mem::take(&mut x.constant_args[0].0).as_str_ref().unwrap().into()), *pos);
} }
// Do not call some special keywords // Do not call some special keywords
@ -880,9 +880,9 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
// Call built-in operators // Call built-in operators
Expr::FnCall(x, pos) Expr::FnCall(x, pos)
if x.namespace.is_none() // Non-qualified if !x.is_qualified() // Non-qualified
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations && state.optimization_level == OptimizationLevel::Simple // simple optimizations
&& x.num_args() == 2 // binary call && x.args_count() == 2 // binary call
&& x.args.iter().all(Expr::is_constant) // all arguments are constants && x.args.iter().all(Expr::is_constant) // all arguments are constants
//&& !is_valid_identifier(x.name.chars()) // cannot be scripted //&& !is_valid_identifier(x.name.chars()) // cannot be scripted
=> { => {
@ -922,13 +922,13 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
// Eagerly call functions // Eagerly call functions
Expr::FnCall(x, pos) Expr::FnCall(x, pos)
if x.namespace.is_none() // Non-qualified if !x.is_qualified() // Non-qualified
&& state.optimization_level == OptimizationLevel::Full // full optimizations && state.optimization_level == OptimizationLevel::Full // full optimizations
&& x.args.iter().all(Expr::is_constant) // all arguments are constants && x.args.iter().all(Expr::is_constant) // all arguments are constants
=> { => {
// First search for script-defined functions (can override built-in) // First search for script-defined functions (can override built-in)
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let has_script_fn = state.lib.iter().any(|&m| m.get_script_fn(x.name.as_ref(), x.num_args()).is_some()); let has_script_fn = state.lib.iter().any(|&m| m.get_script_fn(x.name.as_ref(), x.args_count()).is_some());
#[cfg(feature = "no_function")] #[cfg(feature = "no_function")]
let has_script_fn = false; let has_script_fn = false;

View File

@ -1596,8 +1596,8 @@ fn make_dot_expr(
Expr::FnCall(mut func, func_pos) => { Expr::FnCall(mut func, func_pos) => {
// Recalculate hash // Recalculate hash
func.hashes = FnCallHashes::from_script_and_native( func.hashes = FnCallHashes::from_script_and_native(
calc_fn_hash(empty(), &func.name, func.num_args()), calc_fn_hash(empty(), &func.name, func.args_count()),
calc_fn_hash(empty(), &func.name, func.num_args() + 1), calc_fn_hash(empty(), &func.name, func.args_count() + 1),
); );
let rhs = Expr::Dot( let rhs = Expr::Dot(
@ -1623,7 +1623,7 @@ fn make_dot_expr(
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
} }
// lhs.nnn::func(...) // lhs.nnn::func(...)
(_, Expr::FnCall(x, _)) if x.namespace.is_some() => { (_, Expr::FnCall(x, _)) if x.is_qualified() => {
unreachable!("method call should not be namespace-qualified") unreachable!("method call should not be namespace-qualified")
} }
// lhs.Fn() or lhs.eval() // lhs.Fn() or lhs.eval()
@ -1652,8 +1652,8 @@ fn make_dot_expr(
(lhs, Expr::FnCall(mut func, func_pos)) => { (lhs, Expr::FnCall(mut func, func_pos)) => {
// Recalculate hash // Recalculate hash
func.hashes = FnCallHashes::from_script_and_native( func.hashes = FnCallHashes::from_script_and_native(
calc_fn_hash(empty(), &func.name, func.num_args()), calc_fn_hash(empty(), &func.name, func.args_count()),
calc_fn_hash(empty(), &func.name, func.num_args() + 1), calc_fn_hash(empty(), &func.name, func.args_count() + 1),
); );
let rhs = Expr::FnCall(func, func_pos); let rhs = Expr::FnCall(func, func_pos);
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)