From ce384d8bb0263d5cfbdbf6a68b65043a18e318de Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 20 Apr 2021 23:28:04 +0800 Subject: [PATCH] Change FnPtr to SmartString. --- src/ast.rs | 21 ++++++++++++++------- src/engine.rs | 18 ++++++++++-------- src/fn_native.rs | 2 +- src/optimize.rs | 14 +++++++------- src/parser.rs | 10 +++++----- 5 files changed, 37 insertions(+), 28 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 7ad05bd9..c21439b7 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1444,21 +1444,26 @@ impl FnCallHashes { /// This type is volatile and may change. #[derive(Clone, Default, Hash)] pub struct FnCallExpr { + /// Namespace of the function, if any. + pub namespace: Option, /// Pre-calculated hashes. pub hashes: FnCallHashes, - /// Does this function call capture the parent scope? - pub capture: bool, /// List of function call argument expressions. pub args: StaticVec, /// List of function call arguments that are constants. pub constant_args: smallvec::SmallVec<[(Dynamic, Position); 2]>, - /// Namespace of the function, if any. Boxed because it occurs rarely. - pub namespace: Option, /// Function name. pub name: Identifier, + /// Does this function call capture the parent scope? + pub capture: bool, } 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? #[inline(always)] pub fn is_args_empty(&self) -> bool { @@ -1466,7 +1471,7 @@ impl FnCallExpr { } /// Get the number of arguments to this function call. #[inline(always)] - pub fn num_args(&self) -> usize { + pub fn args_count(&self) -> usize { self.args.len() + self.constant_args.len() } } @@ -1609,7 +1614,7 @@ pub enum Expr { /// [String][ImmutableString] constant. StringConstant(ImmutableString, Position), /// [`FnPtr`] constant. - FnPointer(ImmutableString, Position), + FnPointer(Box, Position), /// An interpolated [string][ImmutableString]. InterpolatedString(Box>), /// [ expr, ... ] @@ -1760,7 +1765,9 @@ impl Expr { Self::FloatConstant(x, _) => (*x).into(), Self::CharConstant(x, _) => (*x).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::Unit(_) => Dynamic::UNIT, diff --git a/src/engine.rs b/src/engine.rs index 2d7ba3dd..a21af6be 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1227,7 +1227,7 @@ impl Engine { ChainType::Dot => { match rhs { // 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 mut args = idx_val.as_fn_call_args(); 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 - 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 mut args = idx_val.as_fn_call_args(); let (val, _) = self.make_method_call( @@ -1394,7 +1394,7 @@ impl Engine { Ok((result, may_be_changed)) } // 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 mut args = idx_val.as_fn_call_args(); let (mut val, _) = self.make_method_call( @@ -1508,7 +1508,7 @@ impl Engine { self.inc_operations(state, expr.position())?; 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_values = x @@ -1547,7 +1547,7 @@ impl Engine { } Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), 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(); @@ -1749,7 +1749,9 @@ impl Engine { Expr::FloatConstant(x, _) => Ok((*x).into()), Expr::StringConstant(x, _) => Ok(x.clone().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 .as_deref() @@ -1829,7 +1831,7 @@ impl Engine { } // Normal function call - Expr::FnCall(x, pos) if x.namespace.is_none() => { + Expr::FnCall(x, pos) if !x.is_qualified() => { let FnCallExpr { name, capture, @@ -1845,7 +1847,7 @@ impl Engine { } // Namespace-qualified function call - Expr::FnCall(x, pos) if x.namespace.is_some() => { + Expr::FnCall(x, pos) if x.is_qualified() => { let FnCallExpr { name, namespace, diff --git a/src/fn_native.rs b/src/fn_native.rs index a6c68460..007ba761 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -262,7 +262,7 @@ impl FnPtr { } /// Create a new function pointer without checking its parameters. #[inline(always)] - pub(crate) fn new_unchecked(name: impl Into, curry: StaticVec) -> Self { + pub(crate) fn new_unchecked(name: Identifier, curry: StaticVec) -> Self { Self(name.into(), curry) } /// Get the name of the function. diff --git a/src/optimize.rs b/src/optimize.rs index d026ab53..94ebd31e 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -862,15 +862,15 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { } // Fn Expr::FnCall(x, pos) - if x.namespace.is_none() // Non-qualified + if !x.is_qualified() // Non-qualified && state.optimization_level == OptimizationLevel::Simple // simple optimizations - && x.num_args() == 1 + && x.args_count() == 1 && x.constant_args.len() == 1 && x.constant_args[0].0.is::() && x.name == KEYWORD_FN_PTR => { 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 @@ -880,9 +880,9 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { // Call built-in operators Expr::FnCall(x, pos) - if x.namespace.is_none() // Non-qualified + if !x.is_qualified() // Non-qualified && 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 //&& !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 Expr::FnCall(x, pos) - if x.namespace.is_none() // Non-qualified + if !x.is_qualified() // Non-qualified && state.optimization_level == OptimizationLevel::Full // full optimizations && x.args.iter().all(Expr::is_constant) // all arguments are constants => { // First search for script-defined functions (can override built-in) #[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")] let has_script_fn = false; diff --git a/src/parser.rs b/src/parser.rs index 31c13ece..56841dfa 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1596,8 +1596,8 @@ fn make_dot_expr( Expr::FnCall(mut func, func_pos) => { // Recalculate hash func.hashes = FnCallHashes::from_script_and_native( - calc_fn_hash(empty(), &func.name, func.num_args()), - calc_fn_hash(empty(), &func.name, func.num_args() + 1), + calc_fn_hash(empty(), &func.name, func.args_count()), + calc_fn_hash(empty(), &func.name, func.args_count() + 1), ); let rhs = Expr::Dot( @@ -1623,7 +1623,7 @@ fn make_dot_expr( Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) } // 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") } // lhs.Fn() or lhs.eval() @@ -1652,8 +1652,8 @@ fn make_dot_expr( (lhs, Expr::FnCall(mut func, func_pos)) => { // Recalculate hash func.hashes = FnCallHashes::from_script_and_native( - calc_fn_hash(empty(), &func.name, func.num_args()), - calc_fn_hash(empty(), &func.name, func.num_args() + 1), + calc_fn_hash(empty(), &func.name, func.args_count()), + calc_fn_hash(empty(), &func.name, func.args_count() + 1), ); let rhs = Expr::FnCall(func, func_pos); Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)