Eagerly evaluate built-in operators for OptimizationLevel::Simple.
This commit is contained in:
@@ -439,7 +439,33 @@ impl Engine {
|
||||
}
|
||||
|
||||
// Has a system function an override?
|
||||
fn has_override(&self, lib: &Module, hash_fn: u64, hash_script: u64, pub_only: bool) -> bool {
|
||||
pub(crate) fn has_override_by_name_and_arguments(
|
||||
&self,
|
||||
lib: &Module,
|
||||
name: &str,
|
||||
arg_types: &[TypeId],
|
||||
pub_only: bool,
|
||||
) -> bool {
|
||||
let arg_len = if arg_types.is_empty() {
|
||||
usize::MAX
|
||||
} else {
|
||||
arg_types.len()
|
||||
};
|
||||
|
||||
let hash_fn = calc_fn_hash(empty(), name, arg_len, arg_types.iter().cloned());
|
||||
let hash_script = calc_fn_hash(empty(), name, arg_types.len(), empty());
|
||||
|
||||
self.has_override(lib, hash_fn, hash_script, pub_only)
|
||||
}
|
||||
|
||||
// Has a system function an override?
|
||||
pub(crate) fn has_override(
|
||||
&self,
|
||||
lib: &Module,
|
||||
hash_fn: u64,
|
||||
hash_script: u64,
|
||||
pub_only: bool,
|
||||
) -> bool {
|
||||
// NOTE: We skip script functions for global_module and packages, and native functions for lib
|
||||
|
||||
// First check script-defined functions
|
||||
|
@@ -294,6 +294,23 @@ impl Module {
|
||||
hash_script
|
||||
}
|
||||
|
||||
/// Get a script-defined function in the module based on name and number of parameters.
|
||||
pub fn get_script_fn(
|
||||
&self,
|
||||
name: &str,
|
||||
num_params: usize,
|
||||
public_only: bool,
|
||||
) -> Option<&Shared<ScriptFnDef>> {
|
||||
self.functions
|
||||
.values()
|
||||
.find(|(fn_name, access, num, _, _)| {
|
||||
(!public_only || *access == FnAccess::Public)
|
||||
&& *num == num_params
|
||||
&& fn_name == name
|
||||
})
|
||||
.map(|(_, _, _, _, f)| f.get_shared_fn_def())
|
||||
}
|
||||
|
||||
/// Does a sub-module exist in the module?
|
||||
///
|
||||
/// # Examples
|
||||
|
102
src/optimize.rs
102
src/optimize.rs
@@ -5,6 +5,7 @@ use crate::calc_fn_hash;
|
||||
use crate::engine::{
|
||||
Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
||||
};
|
||||
use crate::fn_call::run_builtin_binary_op;
|
||||
use crate::fn_native::FnPtr;
|
||||
use crate::module::Module;
|
||||
use crate::parser::{map_dynamic_to_expr, Expr, ScriptFnDef, Stmt, AST};
|
||||
@@ -568,58 +569,75 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
||||
}
|
||||
}
|
||||
|
||||
// Call built-in functions
|
||||
Expr::FnCall(mut x)
|
||||
if x.1.is_none() // Non-qualified
|
||||
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
||||
&& x.3.len() == 2 // binary call
|
||||
&& x.3.iter().all(Expr::is_constant) // all arguments are constants
|
||||
=> {
|
||||
let ((name, _, _, pos), _, _, args, _) = x.as_mut();
|
||||
|
||||
let arg_values: StaticVec<_> = args.iter().map(Expr::get_constant_value).collect();
|
||||
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
||||
|
||||
// Search for overloaded operators (can override built-in).
|
||||
if !state.engine.has_override_by_name_and_arguments(state.lib, name, arg_types.as_ref(), false) {
|
||||
if let Some(expr) = run_builtin_binary_op(name, &arg_values[0], &arg_values[1])
|
||||
.ok().flatten()
|
||||
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
||||
{
|
||||
state.set_dirty();
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
|
||||
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||
Expr::FnCall(x)
|
||||
}
|
||||
|
||||
// Eagerly call functions
|
||||
Expr::FnCall(mut x)
|
||||
if x.1.is_none() // Non-qualified
|
||||
&& state.optimization_level == OptimizationLevel::Full // full optimizations
|
||||
&& x.3.iter().all(|expr| expr.is_constant()) // all arguments are constants
|
||||
&& x.3.iter().all(Expr::is_constant) // all arguments are constants
|
||||
=> {
|
||||
let ((name, _, _, pos), _, _, args, def_value) = x.as_mut();
|
||||
|
||||
// First search in functions lib (can override built-in)
|
||||
// Cater for both normal function call style and method call style (one additional arguments)
|
||||
let has_script_fn = cfg!(not(feature = "no_function")) && state.lib.iter_fn().find(|(_, _, _, _,f)| {
|
||||
if !f.is_script() { return false; }
|
||||
let fn_def = f.get_fn_def();
|
||||
fn_def.name == name && (args.len()..=args.len() + 1).contains(&fn_def.params.len())
|
||||
}).is_some();
|
||||
// First search for script-defined functions (can override built-in)
|
||||
let has_script_fn = cfg!(not(feature = "no_function"))
|
||||
&& state.lib.get_script_fn(name, args.len(), false).is_some();
|
||||
|
||||
if has_script_fn {
|
||||
// A script-defined function overrides the built-in function - do not make the call
|
||||
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||
return Expr::FnCall(x);
|
||||
if !has_script_fn {
|
||||
let mut arg_values: StaticVec<_> = args.iter().map(Expr::get_constant_value).collect();
|
||||
|
||||
// Save the typename of the first argument if it is `type_of()`
|
||||
// This is to avoid `call_args` being passed into the closure
|
||||
let arg_for_type_of = if name == KEYWORD_TYPE_OF && arg_values.len() == 1 {
|
||||
state.engine.map_type_name(arg_values[0].type_name())
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
if let Some(expr) = call_fn_with_constant_arguments(&state, name, arg_values.as_mut())
|
||||
.or_else(|| {
|
||||
if !arg_for_type_of.is_empty() {
|
||||
// Handle `type_of()`
|
||||
Some(arg_for_type_of.to_string().into())
|
||||
} else {
|
||||
// Otherwise use the default value, if any
|
||||
def_value.map(|v| v.into())
|
||||
}
|
||||
})
|
||||
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
||||
{
|
||||
state.set_dirty();
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
|
||||
let mut arg_values: StaticVec<_> = args.iter().map(Expr::get_constant_value).collect();
|
||||
|
||||
// Save the typename of the first argument if it is `type_of()`
|
||||
// This is to avoid `call_args` being passed into the closure
|
||||
let arg_for_type_of = if name == KEYWORD_TYPE_OF && arg_values.len() == 1 {
|
||||
state.engine.map_type_name(arg_values[0].type_name())
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
call_fn_with_constant_arguments(&state, name, arg_values.as_mut())
|
||||
.or_else(|| {
|
||||
if !arg_for_type_of.is_empty() {
|
||||
// Handle `type_of()`
|
||||
Some(arg_for_type_of.to_string().into())
|
||||
} else {
|
||||
// Otherwise use the default value, if any
|
||||
def_value.map(|v| v.into())
|
||||
}
|
||||
})
|
||||
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
||||
.map(|expr| {
|
||||
state.set_dirty();
|
||||
expr
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
// Optimize function call arguments
|
||||
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||
Expr::FnCall(x)
|
||||
})
|
||||
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||
Expr::FnCall(x)
|
||||
}
|
||||
|
||||
// id(args ..) -> optimize function call arguments
|
||||
|
Reference in New Issue
Block a user