Change FnPtr to SmartString.
This commit is contained in:
parent
8f1bff2ded
commit
ce384d8bb0
21
src/ast.rs
21
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<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,
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user