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.
#[derive(Clone, Default, Hash)]
pub struct FnCallExpr {
/// Namespace of the function, if any.
pub namespace: Option<NamespaceRef>,
/// 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<Expr>,
/// 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<NamespaceRef>,
/// 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<Identifier>, Position),
/// An interpolated [string][ImmutableString].
InterpolatedString(Box<StaticVec<Expr>>),
/// [ 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,

View File

@ -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,

View File

@ -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<Identifier>, curry: StaticVec<Dynamic>) -> Self {
pub(crate) fn new_unchecked(name: Identifier, curry: StaticVec<Dynamic>) -> Self {
Self(name.into(), curry)
}
/// Get the name of the function.

View File

@ -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::<ImmutableString>()
&& 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;

View File

@ -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)