Skip to content

Commit 28fa8b5

Browse files
authored
Merge pull request #1133 from schungx/master
Better handling of syntactic calls and prevent redefinition
2 parents 7a92f96 + 4a1f871 commit 28fa8b5

10 files changed

Lines changed: 182 additions & 103 deletions

File tree

src/eval/eval_context.rs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -487,25 +487,18 @@ fn _call_fn_raw(
487487
let op_token = Token::lookup_symbol_from_syntax(fn_name);
488488
let op_token = op_token.as_ref();
489489
let args_len = args.len();
490+
let pos = Position::NONE;
490491

491492
if native_only {
492-
if let Some(result) = engine.exec_syntactic_fn_call(fn_name, args, Position::NONE)? {
493+
if let Some(result) = engine.exec_syntactic_fn_call(global, caches, fn_name, args, pos)? {
493494
return Ok(result);
494495
}
495496

496497
let hash = calc_fn_hash(None, fn_name, args_len);
497498

498499
return engine
499500
.exec_native_fn_call(
500-
global,
501-
caches,
502-
fn_name,
503-
op_token,
504-
hash,
505-
args,
506-
is_ref_mut,
507-
false,
508-
Position::NONE,
501+
global, caches, fn_name, op_token, hash, args, is_ref_mut, false, pos,
509502
)
510503
.map(|(r, ..)| r);
511504
}
@@ -534,7 +527,7 @@ fn _call_fn_raw(
534527
args,
535528
is_ref_mut,
536529
is_method_call,
537-
Position::NONE,
530+
pos,
538531
)
539532
.map(|(r, ..)| r)
540533
}

src/func/call.rs

Lines changed: 59 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,10 @@ impl Engine {
352352
) -> RhaiResultOf<(Dynamic, bool)> {
353353
self.track_operation(global, pos)?;
354354

355+
if let Some(result) = self.exec_syntactic_fn_call(global, caches, name, args, pos)? {
356+
return Ok((result, false));
357+
}
358+
355359
// Check if function access already in the cache
356360
let local_entry = &mut None;
357361
let a = Some(&mut *args);
@@ -577,12 +581,14 @@ impl Engine {
577581
}
578582
}
579583

580-
/// Implement built-in functions.
584+
/// Implement built-in syntactic functions.
581585
///
582586
/// These are functions (e.g. `type_of`, `is_shared`) that are not registered as normal
583587
/// functions but provided by Rhai.
584588
pub(crate) fn exec_syntactic_fn_call(
585589
&self,
590+
global: &mut GlobalRuntimeState,
591+
caches: &mut Caches,
586592
fn_name: &str,
587593
args: &FnCallArgs,
588594
pos: Position,
@@ -593,20 +599,68 @@ impl Engine {
593599
let typ = self.get_interned_string(self.map_type_name(args[0].type_name()));
594600
return Ok(Some(typ.into()));
595601
}
602+
KEYWORD_TYPE_OF => (),
596603

604+
// Handle is_shared()
597605
#[cfg(not(feature = "no_closure"))]
598606
crate::engine::KEYWORD_IS_SHARED if args.len() == 1 => {
599607
return Ok(Some(args[0].is_shared().into()))
600608
}
601609
#[cfg(not(feature = "no_closure"))]
602610
crate::engine::KEYWORD_IS_SHARED => (),
603611

612+
// Handle is_def_fn()
613+
#[cfg(not(feature = "no_function"))]
614+
crate::engine::KEYWORD_IS_DEF_FN if args.len() == 2 => {
615+
let fn_name = args[0]
616+
.as_immutable_string_ref()
617+
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?;
618+
let num_params = args[1]
619+
.as_int()
620+
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, pos))?;
621+
622+
return Ok(Some(
623+
usize::try_from(num_params)
624+
.map(|num_params| {
625+
let hash_script = calc_fn_hash(None, &fn_name, num_params);
626+
self.has_script_fn(global, caches, hash_script).into()
627+
})
628+
.unwrap_or(Dynamic::FALSE),
629+
));
630+
}
631+
#[cfg(not(feature = "no_function"))]
632+
#[cfg(not(feature = "no_object"))]
633+
crate::engine::KEYWORD_IS_DEF_FN if args.len() == 3 => {
634+
let this_type = args[0]
635+
.as_immutable_string_ref()
636+
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?;
637+
let fn_name = args[1]
638+
.as_immutable_string_ref()
639+
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?;
640+
let num_params = args[2]
641+
.as_int()
642+
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, pos))?;
643+
644+
return Ok(Some(
645+
usize::try_from(num_params)
646+
.map(|num_params| {
647+
let hash_script = crate::calc_typed_method_hash(
648+
calc_fn_hash(None, &fn_name, num_params),
649+
&this_type,
650+
);
651+
self.has_script_fn(global, caches, hash_script).into()
652+
})
653+
.unwrap_or(Dynamic::FALSE),
654+
));
655+
}
604656
#[cfg(not(feature = "no_function"))]
605657
crate::engine::KEYWORD_IS_DEF_FN => (),
606658

607-
KEYWORD_TYPE_OF | KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR
608-
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY => (),
659+
// Other syntactic functions
660+
KEYWORD_IS_DEF_VAR | KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_FN_PTR_CALL
661+
| KEYWORD_FN_PTR_CURRY => (),
609662

663+
// Normal functions
610664
_ => return Ok(None),
611665
}
612666

@@ -637,10 +691,8 @@ impl Engine {
637691
pos: Position,
638692
) -> RhaiResultOf<(Dynamic, bool)> {
639693
// These may be redirected from method style calls.
640-
if hashes.is_native_only() {
641-
if let Some(result) = self.exec_syntactic_fn_call(fn_name, args, pos)? {
642-
return Ok((result, false));
643-
}
694+
if let Some(result) = self.exec_syntactic_fn_call(global, caches, fn_name, args, pos)? {
695+
return Ok((result, false));
644696
}
645697

646698
// Check for data race.
@@ -1314,74 +1366,6 @@ impl Engine {
13141366
return Ok(arg_value.is_shared().into());
13151367
}
13161368

1317-
// Handle is_def_fn(fn_name, arity)
1318-
#[cfg(not(feature = "no_function"))]
1319-
crate::engine::KEYWORD_IS_DEF_FN if num_args == 2 => {
1320-
let first = first_arg.unwrap();
1321-
let (arg_value, arg_pos) =
1322-
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?;
1323-
1324-
let fn_name = arg_value
1325-
.into_immutable_string()
1326-
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
1327-
1328-
let (arg_value, arg_pos) =
1329-
self.get_arg_value(global, caches, scope, this_ptr, &args_expr[0])?;
1330-
1331-
let num_params = arg_value
1332-
.as_int()
1333-
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, arg_pos))?;
1334-
1335-
return Ok(usize::try_from(num_params)
1336-
.map(|num_params| {
1337-
let hash_script = calc_fn_hash(None, &fn_name, num_params);
1338-
self.has_script_fn(global, caches, hash_script).into()
1339-
})
1340-
.unwrap_or(Dynamic::FALSE));
1341-
}
1342-
1343-
// Handle is_def_fn(this_type, fn_name, arity)
1344-
#[cfg(not(feature = "no_function"))]
1345-
#[cfg(not(feature = "no_object"))]
1346-
crate::engine::KEYWORD_IS_DEF_FN if num_args == 3 => {
1347-
let first = first_arg.unwrap();
1348-
let (arg_value, arg_pos) =
1349-
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?;
1350-
1351-
let this_type = arg_value
1352-
.into_immutable_string()
1353-
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
1354-
1355-
let (arg_value, arg_pos) = self.get_arg_value(
1356-
global,
1357-
caches,
1358-
scope,
1359-
this_ptr.as_deref_mut(),
1360-
&args_expr[0],
1361-
)?;
1362-
1363-
let fn_name = arg_value
1364-
.into_immutable_string()
1365-
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
1366-
1367-
let (arg_value, arg_pos) =
1368-
self.get_arg_value(global, caches, scope, this_ptr, &args_expr[1])?;
1369-
1370-
let num_params = arg_value
1371-
.as_int()
1372-
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, arg_pos))?;
1373-
1374-
return Ok(usize::try_from(num_params)
1375-
.map(|num_params| {
1376-
let hash_script = crate::calc_typed_method_hash(
1377-
calc_fn_hash(None, &fn_name, num_params),
1378-
&this_type,
1379-
);
1380-
self.has_script_fn(global, caches, hash_script).into()
1381-
})
1382-
.unwrap_or(Dynamic::FALSE));
1383-
}
1384-
13851369
// Handle is_def_var(fn_name)
13861370
KEYWORD_IS_DEF_VAR if num_args == 1 => {
13871371
let arg = first_arg.unwrap();

src/func/hashing.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ pub fn calc_fn_hash_full(base: u64, params: impl IntoIterator<Item = TypeId>) ->
152152
}
153153

154154
/// Calculate a [`u64`] hash key from a base [`u64`] hash key and the type of the `this` pointer.
155+
///
156+
/// Not available under `no_object` or `no_function`.
155157
#[cfg(not(feature = "no_object"))]
156158
#[cfg(not(feature = "no_function"))]
157159
#[inline]

src/func/native.rs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -520,28 +520,24 @@ impl<'a> NativeCallContext<'a> {
520520

521521
let fn_name = fn_name.as_ref();
522522
let op_token = Token::lookup_symbol_from_syntax(fn_name);
523+
let op_token = op_token.as_ref();
523524
let args_len = args.len();
525+
let pos = self.call_position();
524526

525527
if native_only {
526-
if let Some(result) =
527-
self.engine()
528-
.exec_syntactic_fn_call(fn_name, args, self.call_position())?
528+
if let Some(result) = self
529+
.engine()
530+
.exec_syntactic_fn_call(global, caches, fn_name, args, pos)?
529531
{
530532
return Ok(result);
531533
}
532534

535+
let hash = calc_fn_hash(None, fn_name, args_len);
536+
533537
return self
534538
.engine()
535539
.exec_native_fn_call(
536-
global,
537-
caches,
538-
fn_name,
539-
op_token.as_ref(),
540-
calc_fn_hash(None, fn_name, args_len),
541-
args,
542-
is_ref_mut,
543-
false,
544-
self.call_position(),
540+
global, caches, fn_name, op_token, hash, args, is_ref_mut, false, pos,
545541
)
546542
.map(|(r, ..)| r);
547543
}
@@ -556,7 +552,7 @@ impl<'a> NativeCallContext<'a> {
556552
),
557553
#[cfg(feature = "no_function")]
558554
true => FnCallHashes::from_native_only(calc_fn_hash(None, fn_name, args_len)),
559-
_ => FnCallHashes::from_hash(calc_fn_hash(None, fn_name, args_len)),
555+
false => FnCallHashes::from_hash(calc_fn_hash(None, fn_name, args_len)),
560556
};
561557

562558
self.engine()
@@ -565,12 +561,12 @@ impl<'a> NativeCallContext<'a> {
565561
caches,
566562
None,
567563
fn_name,
568-
op_token.as_ref(),
564+
op_token,
569565
hash,
570566
args,
571567
is_ref_mut,
572568
is_method_call,
573-
self.call_position(),
569+
pos,
574570
)
575571
.map(|(r, ..)| r)
576572
}

src/module/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ impl FuncRegistration {
217217
///
218218
/// * **Metadata**: No metadata for the function is registered.
219219
///
220+
/// # Example
220221
/// ```
221222
/// # use rhai::{Module, FuncRegistration, FnNamespace};
222223
/// let mut module = Module::new();

src/packages/debugging.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::prelude::v1::*;
77

88
#[cfg(not(feature = "no_function"))]
99
#[cfg(not(feature = "no_index"))]
10-
use crate::{Array, Dynamic, NativeCallContext};
10+
use crate::{Array, NativeCallContext};
1111

1212
#[cfg(not(feature = "no_function"))]
1313
#[cfg(not(feature = "no_index"))]
@@ -75,7 +75,7 @@ mod debugging_functions {
7575
(_pos.position().unwrap_or(0) as INT).into(),
7676
);
7777
}
78-
Dynamic::from_map(map)
78+
crate::Dynamic::from_map(map)
7979
}
8080
#[cfg(feature = "no_object")]
8181
display.into()

tests/closures.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,36 @@ fn test_closures() {
199199
}
200200
}
201201

202+
#[test]
203+
#[cfg(not(feature = "no_closure"))]
204+
fn test_closures_is_shared_registration() {
205+
let mut engine = Engine::new();
206+
207+
engine.register_fn("is_shared", |name: &str| name.to_string());
208+
209+
assert!(engine
210+
.eval::<bool>(
211+
"
212+
let a = 41;
213+
let foo = |x| { a += x };
214+
is_shared(a)
215+
"
216+
)
217+
.unwrap());
218+
219+
engine.register_fn("is_shared", |name: &str, _: bool| name.to_string());
220+
221+
assert!(engine
222+
.eval::<String>(
223+
r#"
224+
let a = 41;
225+
let foo = |x| { a += x };
226+
is_shared(a, true)
227+
"#
228+
)
229+
.is_err());
230+
}
231+
202232
#[test]
203233
#[cfg(not(feature = "no_closure"))]
204234
fn test_closures_sharing() {

tests/functions.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,33 @@ fn test_functions_is_def() {
539539
.unwrap());
540540
}
541541

542+
#[test]
543+
fn test_functions_is_def_registration() {
544+
let mut engine = Engine::new();
545+
546+
engine.register_fn("is_def_fn", |name: &str, _: INT| name.to_string());
547+
548+
assert!(engine
549+
.eval::<bool>(
550+
r#"
551+
fn foo(x) { x + 1 }
552+
is_def_fn("foo", 1)
553+
"#
554+
)
555+
.unwrap());
556+
557+
engine.register_fn("is_def_fn", |name: &str| name.to_string());
558+
559+
assert!(engine
560+
.eval::<String>(
561+
r#"
562+
fn foo() { 1 }
563+
is_def_fn("foo")
564+
"#
565+
)
566+
.is_err());
567+
}
568+
542569
#[test]
543570
#[cfg(not(feature = "unchecked"))]
544571
fn test_functions_max() {

0 commit comments

Comments
 (0)