rhai/eval/
stmt.rs

1//! Module defining functions for evaluating a statement.
2
3use super::{Caches, EvalContext, GlobalRuntimeState, Target};
4use crate::ast::{
5    ASTFlags, BinaryExpr, Expr, FlowControl, OpAssignment, Stmt, SwitchCasesCollection,
6};
7use crate::func::{get_builtin_op_assignment_fn, get_hasher};
8use crate::tokenizer::Token;
9use crate::types::dynamic::{AccessMode, Union};
10use crate::{Dynamic, Engine, RhaiResult, RhaiResultOf, Scope, VarDefInfo, ERR, INT};
11use std::hash::{Hash, Hasher};
12#[cfg(feature = "no_std")]
13use std::prelude::v1::*;
14
15impl Engine {
16    /// If the value is a string, intern it.
17    #[inline(always)]
18    fn intern_string(&self, value: Dynamic) -> Dynamic {
19        match value.0 {
20            Union::Str(s, ..) => self.get_interned_string(s).into(),
21            _ => value,
22        }
23    }
24
25    /// Evaluate a statements block.
26    pub(crate) fn eval_stmt_block(
27        &self,
28        global: &mut GlobalRuntimeState,
29        caches: &mut Caches,
30        scope: &mut Scope,
31        mut this_ptr: Option<&mut Dynamic>,
32        statements: &[Stmt],
33        restore_orig_state: bool,
34    ) -> RhaiResult {
35        if statements.is_empty() {
36            return Ok(Dynamic::UNIT);
37        }
38
39        // Restore scope at end of block if necessary
40        defer! { scope if restore_orig_state => rewind; let orig_scope_len = scope.len(); }
41
42        // Restore global state at end of block if necessary
43        let orig_always_search_scope = global.always_search_scope;
44        #[cfg(not(feature = "no_module"))]
45        let orig_imports_len = global.num_imports();
46
47        if restore_orig_state {
48            global.scope_level += 1;
49        }
50
51        defer! { global if restore_orig_state => move |g| {
52            g.scope_level -= 1;
53
54            #[cfg(not(feature = "no_module"))]
55            g.truncate_imports(orig_imports_len);
56
57            // The impact of new local variables goes away at the end of a block
58            // because any new variables introduced will go out of scope
59            g.always_search_scope = orig_always_search_scope;
60        }}
61
62        // Pop new function resolution caches at end of block
63        defer! {
64            caches => rewind_fn_resolution_caches;
65            let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len();
66        }
67
68        // Run the statements
69        statements.iter().try_fold(Dynamic::UNIT, |_, stmt| {
70            let this_ptr = this_ptr.as_deref_mut();
71
72            #[cfg(not(feature = "no_module"))]
73            let orig_imports_len = global.num_imports();
74
75            let result =
76                self.eval_stmt(global, caches, scope, this_ptr, stmt, restore_orig_state)?;
77
78            #[cfg(not(feature = "no_module"))]
79            if matches!(stmt, Stmt::Import(..)) {
80                // Get the extra modules - see if any functions are marked global.
81                // Without global functions, the extra modules never affect function resolution.
82                if global
83                    .scan_imports_raw()
84                    .skip(orig_imports_len)
85                    .any(|(.., m)| m.contains_indexed_global_functions())
86                {
87                    // Different scenarios where the cache must be cleared - notice that this is
88                    // expensive as all function resolutions must start again
89                    if caches.fn_resolution_caches_len() > orig_fn_resolution_caches_len {
90                        // When new module is imported with global functions and there is already
91                        // a new cache, just clear it
92                        caches.fn_resolution_cache_mut().clear();
93                    } else if restore_orig_state {
94                        // When new module is imported with global functions, push a new cache
95                        caches.push_fn_resolution_cache();
96                    } else {
97                        // When the block is to be evaluated in-place, just clear the current cache
98                        caches.fn_resolution_cache_mut().clear();
99                    }
100                }
101            }
102
103            Ok(result)
104        })
105    }
106
107    /// Evaluate an op-assignment statement.
108    pub(crate) fn eval_op_assignment(
109        &self,
110        global: &mut GlobalRuntimeState,
111        caches: &mut Caches,
112        op_info: &OpAssignment,
113        root: &Expr,
114        target: &mut Target,
115        mut new_val: Dynamic,
116    ) -> RhaiResultOf<()> {
117        // Assignment to constant variable?
118        if target.as_ref().is_read_only() {
119            let name = root.get_variable_name(false).unwrap_or_default();
120            let pos = root.start_position();
121            return Err(ERR::ErrorAssignmentToConstant(name.to_string(), pos).into());
122        }
123
124        let pos = op_info.position();
125
126        if let Some((hash_x, hash, op_x, op_x_str, op, op_str)) = op_info.get_op_assignment_info() {
127            let mut lock_guard = target.as_mut().write_lock::<Dynamic>().unwrap();
128            let mut done = false;
129
130            // Short-circuit built-in op-assignments if under Fast Operators mode
131            if self.fast_operators() {
132                #[allow(clippy::wildcard_imports)]
133                use Token::*;
134
135                done = true;
136
137                // For extremely simple primary data operations, do it directly
138                // to avoid the overhead of calling a function.
139                match (&mut lock_guard.0, &mut new_val.0) {
140                    (Union::Bool(b1, ..), Union::Bool(b2, ..)) => match op_x {
141                        AndAssign => *b1 = *b1 && *b2,
142                        OrAssign => *b1 = *b1 || *b2,
143                        XOrAssign => *b1 ^= *b2,
144                        _ => done = false,
145                    },
146                    (Union::Int(n1, ..), Union::Int(n2, ..)) => {
147                        #[cfg(not(feature = "unchecked"))]
148                        #[allow(clippy::wildcard_imports)]
149                        use crate::packages::arithmetic::arith_basic::INT::functions::*;
150
151                        #[cfg(not(feature = "unchecked"))]
152                        match op_x {
153                            PlusAssign => {
154                                *n1 = add(*n1, *n2).map_err(|err| err.fill_position(pos))?
155                            }
156                            MinusAssign => {
157                                *n1 = subtract(*n1, *n2).map_err(|err| err.fill_position(pos))?
158                            }
159                            MultiplyAssign => {
160                                *n1 = multiply(*n1, *n2).map_err(|err| err.fill_position(pos))?
161                            }
162                            DivideAssign => {
163                                *n1 = divide(*n1, *n2).map_err(|err| err.fill_position(pos))?
164                            }
165                            ModuloAssign => {
166                                *n1 = modulo(*n1, *n2).map_err(|err| err.fill_position(pos))?
167                            }
168                            _ => done = false,
169                        }
170                        #[cfg(feature = "unchecked")]
171                        match op_x {
172                            PlusAssign => *n1 += *n2,
173                            MinusAssign => *n1 -= *n2,
174                            MultiplyAssign => *n1 *= *n2,
175                            DivideAssign => *n1 /= *n2,
176                            ModuloAssign => *n1 %= *n2,
177                            _ => done = false,
178                        }
179                    }
180                    #[cfg(not(feature = "no_float"))]
181                    (Union::Float(f1, ..), Union::Float(f2, ..)) => match op_x {
182                        PlusAssign => **f1 += **f2,
183                        MinusAssign => **f1 -= **f2,
184                        MultiplyAssign => **f1 *= **f2,
185                        DivideAssign => **f1 /= **f2,
186                        ModuloAssign => **f1 %= **f2,
187                        _ => done = false,
188                    },
189                    #[cfg(not(feature = "no_float"))]
190                    (Union::Float(f1, ..), Union::Int(n2, ..)) => match op_x {
191                        PlusAssign => **f1 += *n2 as crate::FLOAT,
192                        MinusAssign => **f1 -= *n2 as crate::FLOAT,
193                        MultiplyAssign => **f1 *= *n2 as crate::FLOAT,
194                        DivideAssign => **f1 /= *n2 as crate::FLOAT,
195                        ModuloAssign => **f1 %= *n2 as crate::FLOAT,
196                        _ => done = false,
197                    },
198                    _ => done = false,
199                }
200
201                if !done {
202                    if let Some((func, need_context)) =
203                        get_builtin_op_assignment_fn(op_x, &lock_guard, &new_val)
204                    {
205                        // We may not need to bump the level because built-in's do not need it.
206                        //defer! { let orig_level = global.level; global.level += 1 }
207
208                        let args = &mut [&mut *lock_guard, &mut new_val];
209                        let context = need_context
210                            .then(|| (self, op_x_str, global.source(), &*global, pos).into());
211                        let _ = func(context, args).map_err(|err| err.fill_position(pos))?;
212                        done = true;
213                    }
214                }
215            }
216
217            if !done {
218                let opx = Some(op_x);
219                let args = &mut [&mut *lock_guard, &mut new_val];
220
221                match self.exec_native_fn_call(
222                    global, caches, op_x_str, opx, hash_x, args, true, false, pos,
223                ) {
224                    Ok(_) => (),
225                    Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_x_str)) =>
226                    {
227                        // Expand to `var = var op rhs`
228                        let op = Some(op);
229
230                        *args[0] = self
231                            .exec_native_fn_call(
232                                global, caches, op_str, op, hash, args, true, false, pos,
233                            )?
234                            .0;
235                    }
236                    Err(err) => return Err(err),
237                }
238
239                self.check_data_size(&*args[0], root.position())?;
240            }
241        } else {
242            // Normal assignment
243            match target {
244                // Lock it again just in case it is shared
245                Target::RefMut(_) | Target::TempValue(_) => {
246                    *target.as_mut().write_lock::<Dynamic>().unwrap() = new_val
247                }
248                #[allow(unreachable_patterns)]
249                _ => *target.as_mut() = new_val,
250            }
251        }
252
253        target.propagate_changed_value(pos)
254    }
255
256    /// Evaluate a statement.
257    pub(crate) fn eval_stmt(
258        &self,
259        global: &mut GlobalRuntimeState,
260        caches: &mut Caches,
261        scope: &mut Scope,
262        mut this_ptr: Option<&mut Dynamic>,
263        stmt: &Stmt,
264        rewind_scope: bool,
265    ) -> RhaiResult {
266        self.track_operation(global, stmt.position())?;
267
268        #[cfg(feature = "debugging")]
269        let reset = self.dbg_reset(global, caches, scope, this_ptr.as_deref_mut(), stmt)?;
270        #[cfg(feature = "debugging")]
271        defer! { global if Some(reset) => move |g| g.debugger_mut().reset_status(reset) }
272
273        match stmt {
274            // No-op
275            Stmt::Noop(..) => Ok(Dynamic::UNIT),
276
277            // Expression as statement
278            Stmt::Expr(expr) => self
279                .eval_expr(global, caches, scope, this_ptr, expr)
280                .map(Dynamic::flatten),
281
282            // Block scope
283            Stmt::Block(stmts, ..) => {
284                if stmts.is_empty() {
285                    Ok(Dynamic::UNIT)
286                } else {
287                    self.eval_stmt_block(global, caches, scope, this_ptr, stmts.statements(), true)
288                }
289            }
290
291            // Function call
292            Stmt::FnCall(x, pos) => {
293                self.eval_fn_call_expr(global, caches, scope, this_ptr, x, *pos)
294            }
295
296            // Assignment
297            Stmt::Assignment(x, ..) => {
298                let (op_info, BinaryExpr { lhs, rhs }) = &**x;
299
300                if let Expr::ThisPtr(..) = lhs {
301                    if this_ptr.is_none() {
302                        return Err(ERR::ErrorUnboundThis(lhs.position()).into());
303                    }
304
305                    #[cfg(not(feature = "no_function"))]
306                    {
307                        use std::convert::TryInto;
308
309                        let rhs_val = self
310                            .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), rhs)?
311                            .flatten();
312
313                        self.track_operation(global, lhs.position())?;
314
315                        let target = &mut this_ptr.unwrap().try_into()?;
316
317                        self.eval_op_assignment(global, caches, op_info, lhs, target, rhs_val)?;
318                    }
319                    #[cfg(feature = "no_function")]
320                    unreachable!();
321                } else if let Expr::Variable(x, ..) = lhs {
322                    let rhs_val = self
323                        .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), rhs)?
324                        .flatten();
325
326                    self.track_operation(global, lhs.position())?;
327
328                    let mut target = self.search_namespace(global, caches, scope, this_ptr, lhs)?;
329
330                    let is_temp_result = !target.is_ref();
331
332                    #[cfg(not(feature = "no_closure"))]
333                    // Also handle case where target is a `Dynamic` shared value
334                    // (returned by a variable resolver, for example)
335                    let is_temp_result = is_temp_result && !target.is_shared();
336
337                    // Cannot assign to temp result from expression
338                    if is_temp_result {
339                        return Err(ERR::ErrorAssignmentToConstant(
340                            x.1.to_string(),
341                            lhs.position(),
342                        )
343                        .into());
344                    }
345
346                    self.eval_op_assignment(global, caches, op_info, lhs, &mut target, rhs_val)?;
347                } else {
348                    #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
349                    {
350                        let rhs_val = self
351                            .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), rhs)?
352                            .flatten();
353                        let _new_val = Some((self.intern_string(rhs_val), op_info));
354
355                        // Must be either `var[index] op= val` or `var.prop op= val`.
356                        // The return value of any op-assignment (should be `()`) is thrown away and not used.
357                        let _ = match lhs {
358                            // this op= rhs (handled above)
359                            Expr::ThisPtr(..) => {
360                                unreachable!("Expr::ThisPtr case is already handled")
361                            }
362                            // name op= rhs (handled above)
363                            Expr::Variable(..) => {
364                                unreachable!("Expr::Variable case is already handled")
365                            }
366                            // idx_lhs[idx_expr] op= rhs
367                            #[cfg(not(feature = "no_index"))]
368                            Expr::Index(..) => self.eval_dot_index_chain(
369                                global, caches, scope, this_ptr, lhs, _new_val,
370                            ),
371                            // dot_lhs.dot_rhs op= rhs
372                            #[cfg(not(feature = "no_object"))]
373                            Expr::Dot(..) => self.eval_dot_index_chain(
374                                global, caches, scope, this_ptr, lhs, _new_val,
375                            ),
376                            _ => unreachable!("cannot assign to expression: {:?}", lhs),
377                        }?;
378                    }
379                }
380
381                Ok(Dynamic::UNIT)
382            }
383
384            // Variable definition
385            Stmt::Var(x, options, pos) => {
386                if !self.allow_shadowing() && scope.contains(x.0.as_str()) {
387                    return Err(ERR::ErrorVariableExists(x.0.as_str().to_string(), *pos).into());
388                }
389
390                // Let/const statement
391                let (var_name, expr, index) = &**x;
392
393                let access = if options.intersects(ASTFlags::CONSTANT) {
394                    AccessMode::ReadOnly
395                } else {
396                    AccessMode::ReadWrite
397                };
398                let export = options.intersects(ASTFlags::EXPORTED);
399
400                // Check variable definition filter
401                if let Some(ref filter) = self.def_var_filter {
402                    let will_shadow = scope.contains(var_name.as_str());
403                    let is_const = access == AccessMode::ReadOnly;
404                    let info = VarDefInfo::new(
405                        var_name.as_str(),
406                        is_const,
407                        global.scope_level,
408                        will_shadow,
409                    );
410                    let orig_scope_len = scope.len();
411                    let context =
412                        EvalContext::new(self, global, caches, scope, this_ptr.as_deref_mut());
413                    let filter_result = filter(true, info, context);
414
415                    if orig_scope_len != scope.len() {
416                        // The scope is changed, always search from now on
417                        global.always_search_scope = true;
418                    }
419
420                    if !filter_result? {
421                        return Err(ERR::ErrorForbiddenVariable(
422                            var_name.as_str().to_string(),
423                            *pos,
424                        )
425                        .into());
426                    }
427                }
428
429                // Guard against too many variables
430                #[cfg(not(feature = "unchecked"))]
431                if index.is_none() && scope.len() >= self.max_variables() {
432                    return Err(ERR::ErrorTooManyVariables(*pos).into());
433                }
434
435                // Evaluate initial value
436                let value = self
437                    .eval_expr(global, caches, scope, this_ptr, expr)?
438                    .flatten();
439                let mut value = self.intern_string(value);
440
441                let _alias = if !rewind_scope {
442                    // Put global constants into global module
443                    #[cfg(not(feature = "no_function"))]
444                    #[cfg(not(feature = "no_module"))]
445                    if global.scope_level == 0
446                        && access == AccessMode::ReadOnly
447                        && global.lib.iter().any(|m| !m.is_empty())
448                    {
449                        crate::func::locked_write(global.constants.get_or_insert_with(|| {
450                            crate::Shared::new(
451                                crate::Locked::new(std::collections::BTreeMap::new()),
452                            )
453                        }))
454                        .unwrap()
455                        .insert(var_name.name.clone(), value.clone());
456                    }
457
458                    export.then_some(var_name)
459                } else if !export {
460                    None
461                } else {
462                    unreachable!("exported variable not on global level");
463                };
464
465                match index {
466                    Some(index) => {
467                        value.set_access_mode(access);
468                        *scope.get_mut_by_index(scope.len() - index.get()) = value;
469                    }
470                    _ => {
471                        scope.push_entry(var_name.name.clone(), access, value);
472                    }
473                }
474
475                #[cfg(not(feature = "no_module"))]
476                if let Some(alias) = _alias {
477                    scope.add_alias_by_index(scope.len() - 1, alias.as_str().into());
478                }
479
480                Ok(Dynamic::UNIT)
481            }
482
483            // If statement
484            Stmt::If(x, ..) => {
485                let FlowControl { expr, body, branch } = &**x;
486
487                let guard_val = self
488                    .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
489                    .as_bool()
490                    .map_err(|typ| self.make_type_mismatch_err::<bool>(typ, expr.position()))?;
491
492                if guard_val && !body.is_empty() {
493                    self.eval_stmt_block(global, caches, scope, this_ptr, body.statements(), true)
494                } else if !guard_val && !branch.is_empty() {
495                    self.eval_stmt_block(global, caches, scope, this_ptr, branch.statements(), true)
496                } else {
497                    Ok(Dynamic::UNIT)
498                }
499            }
500
501            // Switch statement
502            Stmt::Switch(x, ..) => {
503                let (
504                    expr,
505                    SwitchCasesCollection {
506                        expressions,
507                        cases,
508                        def_case,
509                        ranges,
510                    },
511                ) = &**x;
512
513                let mut result = None;
514
515                let value = self.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
516
517                if value.is_hashable() {
518                    let hasher = &mut get_hasher();
519                    value.hash(hasher);
520                    let hash = hasher.finish();
521
522                    // First check hashes
523                    if let Some(case_blocks_list) = cases.get(&hash) {
524                        debug_assert!(!case_blocks_list.is_empty());
525
526                        for &index in case_blocks_list {
527                            let block = &expressions[index];
528
529                            let cond_result = match block.lhs {
530                                Expr::BoolConstant(b, ..) => b,
531                                ref c => self
532                                    .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), c)?
533                                    .as_bool()
534                                    .map_err(|typ| {
535                                        self.make_type_mismatch_err::<bool>(typ, c.position())
536                                    })?,
537                            };
538
539                            if cond_result {
540                                result = Some(&block.rhs);
541                                break;
542                            }
543                        }
544                    } else if !ranges.is_empty() {
545                        // Then check integer ranges
546                        for r in ranges.iter().filter(|r| r.contains(&value)) {
547                            let BinaryExpr { lhs, rhs } = &expressions[r.index()];
548
549                            let cond_result = match lhs {
550                                Expr::BoolConstant(b, ..) => *b,
551                                c => self
552                                    .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), c)?
553                                    .as_bool()
554                                    .map_err(|typ| {
555                                        self.make_type_mismatch_err::<bool>(typ, c.position())
556                                    })?,
557                            };
558
559                            if cond_result {
560                                result = Some(rhs);
561                                break;
562                            }
563                        }
564                    }
565                }
566
567                result
568                    .or_else(|| def_case.as_ref().map(|&index| &expressions[index].rhs))
569                    .map_or(Ok(Dynamic::UNIT), |expr| {
570                        self.eval_expr(global, caches, scope, this_ptr, expr)
571                    })
572            }
573
574            // Loop
575            Stmt::While(x, ..)
576                if matches!(x.expr, Expr::Unit(..) | Expr::BoolConstant(true, ..)) =>
577            {
578                let FlowControl { body, .. } = &**x;
579
580                if body.is_empty() {
581                    loop {
582                        self.track_operation(global, body.position())?;
583                    }
584                }
585
586                loop {
587                    let this_ptr = this_ptr.as_deref_mut();
588                    let statements = body.statements();
589
590                    match self.eval_stmt_block(global, caches, scope, this_ptr, statements, true) {
591                        Ok(..) => (),
592                        Err(err) => match *err {
593                            ERR::LoopBreak(false, ..) => (),
594                            ERR::LoopBreak(true, value, ..) => break Ok(value),
595                            _ => break Err(err),
596                        },
597                    }
598                }
599            }
600
601            // While loop
602            Stmt::While(x, ..) => {
603                let FlowControl { expr, body, .. } = &**x;
604
605                loop {
606                    let condition = self
607                        .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
608                        .as_bool()
609                        .map_err(|typ| self.make_type_mismatch_err::<bool>(typ, expr.position()))?;
610
611                    if !condition {
612                        break Ok(Dynamic::UNIT);
613                    }
614
615                    if body.is_empty() {
616                        continue;
617                    }
618
619                    let this_ptr = this_ptr.as_deref_mut();
620                    let statements = body.statements();
621
622                    match self.eval_stmt_block(global, caches, scope, this_ptr, statements, true) {
623                        Ok(..) => (),
624                        Err(err) => match *err {
625                            ERR::LoopBreak(false, ..) => (),
626                            ERR::LoopBreak(true, value, ..) => break Ok(value),
627                            _ => break Err(err),
628                        },
629                    }
630                }
631            }
632
633            // Do loop
634            Stmt::Do(x, options, ..) => {
635                let FlowControl { expr, body, .. } = &**x;
636                let is_while = !options.intersects(ASTFlags::NEGATED);
637
638                loop {
639                    if !body.is_empty() {
640                        let this_ptr = this_ptr.as_deref_mut();
641                        let statements = body.statements();
642
643                        match self
644                            .eval_stmt_block(global, caches, scope, this_ptr, statements, true)
645                        {
646                            Ok(..) => (),
647                            Err(err) => match *err {
648                                ERR::LoopBreak(false, ..) => continue,
649                                ERR::LoopBreak(true, value, ..) => break Ok(value),
650                                _ => break Err(err),
651                            },
652                        }
653                    }
654
655                    let condition = self
656                        .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
657                        .as_bool()
658                        .map_err(|typ| self.make_type_mismatch_err::<bool>(typ, expr.position()))?;
659
660                    if condition ^ is_while {
661                        break Ok(Dynamic::UNIT);
662                    }
663                }
664            }
665
666            // For loop
667            Stmt::For(x, ..) => {
668                let (var_name, counter, FlowControl { expr, body, .. }) = &**x;
669
670                // Guard against too many variables
671                #[cfg(not(feature = "unchecked"))]
672                if scope.len() >= self.max_variables() - usize::from(counter.is_some()) {
673                    return Err(ERR::ErrorTooManyVariables(var_name.pos).into());
674                }
675
676                let iter_obj = self
677                    .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
678                    .flatten();
679
680                let iter_type = iter_obj.type_id();
681
682                // lib should only contain scripts, so technically they cannot have iterators
683
684                // Search order:
685                // 1) Global namespace - functions registered via Engine::register_XXX
686                // 2) Global modules - packages
687                // 3) Imported modules - functions marked with global namespace
688                // 4) Global sub-modules - functions marked with global namespace
689                let iter_func = self
690                    .global_modules
691                    .iter()
692                    .find_map(|m| m.get_iter(iter_type));
693
694                #[cfg(not(feature = "no_module"))]
695                let iter_func = iter_func
696                    .or_else(|| global.get_iter(iter_type))
697                    .or_else(|| {
698                        self.global_sub_modules
699                            .values()
700                            .find_map(|m| m.get_qualified_iter(iter_type))
701                    });
702
703                let iter_func = iter_func.ok_or_else(|| ERR::ErrorFor(expr.start_position()))?;
704
705                // Restore scope at end of statement
706                defer! { scope => rewind; let orig_scope_len = scope.len(); }
707
708                // Add the loop variables
709                let counter_index = counter.as_ref().map(|counter| {
710                    scope.push(counter.name.clone(), 0 as INT);
711                    scope.len() - 1
712                });
713
714                scope.push(var_name.name.clone(), ());
715                let index = scope.len() - 1;
716
717                let mut result = Dynamic::UNIT;
718
719                if body.is_empty() {
720                    for _ in iter_func(iter_obj) {
721                        self.track_operation(global, body.position())?;
722                    }
723                } else {
724                    let mut index_value: INT = -1;
725
726                    for iter_value in iter_func(iter_obj) {
727                        #[cfg(not(feature = "unchecked"))]
728                        {
729                            index_value = index_value.checked_add(1).ok_or_else(|| {
730                                ERR::ErrorArithmetic(
731                                    format!("for-loop counter overflow: {index_value}"),
732                                    var_name.pos,
733                                )
734                            })?;
735                        }
736                        #[cfg(feature = "unchecked")]
737                        {
738                            index_value += 1;
739                        }
740
741                        // Increment counter
742                        if let Some(counter_index) = counter_index {
743                            *scope.get_mut_by_index(counter_index).write_lock().unwrap() =
744                                Dynamic::from_int(index_value);
745                        }
746
747                        // Set loop value
748                        let value = iter_value
749                            .map_err(|err| err.fill_position(expr.position()))?
750                            .flatten();
751
752                        *scope.get_mut_by_index(index).write_lock().unwrap() = value;
753
754                        // Run block
755                        let this_ptr = this_ptr.as_deref_mut();
756                        let statements = body.statements();
757
758                        match self
759                            .eval_stmt_block(global, caches, scope, this_ptr, statements, true)
760                        {
761                            Ok(_) => (),
762                            Err(err) => match *err {
763                                ERR::LoopBreak(false, ..) => (),
764                                ERR::LoopBreak(true, value, ..) => {
765                                    result = value;
766                                    break;
767                                }
768                                _ => return Err(err),
769                            },
770                        }
771                    }
772                }
773
774                Ok(result)
775            }
776
777            // Continue/Break statement
778            Stmt::BreakLoop(expr, options, pos) => {
779                let is_break = options.intersects(ASTFlags::BREAK);
780
781                let value = match expr {
782                    Some(ref expr) => self.eval_expr(global, caches, scope, this_ptr, expr)?,
783                    None => Dynamic::UNIT,
784                };
785
786                Err(ERR::LoopBreak(is_break, value, *pos).into())
787            }
788
789            // Try/Catch statement
790            Stmt::TryCatch(x, ..) => {
791                let FlowControl {
792                    body,
793                    expr: catch_var,
794                    branch,
795                } = &**x;
796
797                match self.eval_stmt_block(
798                    global,
799                    caches,
800                    scope,
801                    this_ptr.as_deref_mut(),
802                    body.statements(),
803                    true,
804                ) {
805                    r @ Ok(_) => r,
806                    Err(err) if err.is_pseudo_error() => Err(err),
807                    Err(err) if !err.is_catchable() => Err(err),
808                    Err(mut err) => {
809                        let err_value = match err.unwrap_inner() {
810                            // No error variable
811                            _ if catch_var.is_unit() => Dynamic::UNIT,
812
813                            ERR::ErrorRuntime(x, ..) => x.clone(),
814
815                            #[cfg(feature = "no_object")]
816                            _ => {
817                                let _ = err.take_position();
818                                err.to_string().into()
819                            }
820                            #[cfg(not(feature = "no_object"))]
821                            _ => {
822                                let mut err_map = crate::Map::new();
823                                let err_pos = err.take_position();
824
825                                err_map.insert("message".into(), err.to_string().into());
826
827                                if let Some(ref source) = global.source {
828                                    err_map.insert("source".into(), source.into());
829                                }
830
831                                if !err_pos.is_none() {
832                                    err_map.insert(
833                                        "line".into(),
834                                        (err_pos.line().unwrap() as INT).into(),
835                                    );
836                                    err_map.insert(
837                                        "position".into(),
838                                        (err_pos.position().unwrap_or(0) as INT).into(),
839                                    );
840                                }
841
842                                err.dump_fields(&mut err_map);
843                                err_map.into()
844                            }
845                        };
846
847                        // Restore scope at end of block
848                        defer! { scope if !catch_var.is_unit() => rewind; let orig_scope_len = scope.len(); }
849
850                        if let Expr::Variable(x, ..) = catch_var {
851                            // Guard against too many variables
852                            #[cfg(not(feature = "unchecked"))]
853                            if scope.len() >= self.max_variables() {
854                                return Err(ERR::ErrorTooManyVariables(catch_var.position()).into());
855                            }
856                            scope.push(x.1.clone(), err_value);
857                        }
858
859                        let this_ptr = this_ptr.as_deref_mut();
860                        let statements = branch.statements();
861
862                        self.eval_stmt_block(global, caches, scope, this_ptr, statements, true)
863                            .map(|_| Dynamic::UNIT)
864                            .map_err(|result_err| match *result_err {
865                                // Re-throw exception
866                                ERR::ErrorRuntime(v, pos) if v.is_unit() => {
867                                    err.set_position(pos);
868                                    err
869                                }
870                                _ => result_err,
871                            })
872                    }
873                }
874            }
875
876            // Throw value
877            Stmt::Return(Some(expr), options, pos) if options.intersects(ASTFlags::BREAK) => self
878                .eval_expr(global, caches, scope, this_ptr, expr)
879                .and_then(|v| Err(ERR::ErrorRuntime(v.flatten(), *pos).into())),
880
881            // Empty throw
882            Stmt::Return(None, options, pos) if options.intersects(ASTFlags::BREAK) => {
883                Err(ERR::ErrorRuntime(Dynamic::UNIT, *pos).into())
884            }
885
886            // Return value
887            Stmt::Return(Some(expr), .., pos) => self
888                .eval_expr(global, caches, scope, this_ptr, expr)
889                .and_then(|v| Err(ERR::Return(v.flatten(), *pos).into())),
890
891            // Empty return
892            Stmt::Return(None, .., pos) => Err(ERR::Return(Dynamic::UNIT, *pos).into()),
893
894            // Import statement
895            #[cfg(not(feature = "no_module"))]
896            Stmt::Import(x, _pos) => {
897                use crate::ModuleResolver;
898
899                let (expr, export) = &**x;
900
901                // Guard against too many modules
902                #[cfg(not(feature = "unchecked"))]
903                if global.num_modules_loaded >= self.max_modules() {
904                    return Err(ERR::ErrorTooManyModules(*_pos).into());
905                }
906
907                let v = self.eval_expr(global, caches, scope, this_ptr, expr)?;
908
909                let path = v.try_cast_result::<crate::ImmutableString>().map_err(|v| {
910                    self.make_type_mismatch_err::<crate::ImmutableString>(
911                        v.type_name(),
912                        expr.position(),
913                    )
914                })?;
915
916                let path_pos = expr.start_position();
917
918                let resolver = global.embedded_module_resolver.clone();
919
920                let module = resolver
921                    .as_ref()
922                    .and_then(
923                        |r| match r.resolve_raw(self, global, scope, &path, path_pos) {
924                            Err(err) if matches!(*err, ERR::ErrorModuleNotFound(..)) => None,
925                            result => Some(result),
926                        },
927                    )
928                    .or_else(|| {
929                        match self
930                            .module_resolver()
931                            .resolve_raw(self, global, scope, &path, path_pos)
932                        {
933                            Err(err) if matches!(*err, ERR::ErrorModuleNotFound(..)) => None,
934                            result => Some(result),
935                        }
936                    })
937                    .unwrap_or_else(|| {
938                        Err(ERR::ErrorModuleNotFound(path.to_string(), path_pos).into())
939                    })?;
940
941                let (export, must_be_indexed) = if export.is_empty() {
942                    (self.const_empty_string(), false)
943                } else {
944                    (export.name.clone(), true)
945                };
946
947                if !must_be_indexed || module.is_indexed() {
948                    global.push_import(export, module);
949                } else {
950                    // Index the module (making a clone copy if necessary) if it is not indexed
951                    let mut m = crate::func::shared_take_or_clone(module);
952                    m.build_index();
953                    global.push_import(export, m);
954                }
955
956                global.num_modules_loaded += 1;
957
958                Ok(Dynamic::UNIT)
959            }
960
961            // Export statement
962            #[cfg(not(feature = "no_module"))]
963            Stmt::Export(x, ..) => {
964                use crate::ast::Ident;
965                let (Ident { name, pos, .. }, Ident { name: alias, .. }) = &**x;
966                // Mark scope variables as public
967                scope.search(name).map_or_else(
968                    || Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into()),
969                    |index| {
970                        let alias = if alias.is_empty() { name } else { alias };
971                        scope.add_alias_by_index(index, alias.clone());
972                        Ok(Dynamic::UNIT)
973                    },
974                )
975            }
976
977            // Share statement
978            #[cfg(not(feature = "no_closure"))]
979            Stmt::Share(x) => {
980                for (var, index) in &**x {
981                    // Check the variable resolver, if any
982                    if let Some(ref resolve_var) = self.resolve_var {
983                        let orig_scope_len = scope.len();
984
985                        let context =
986                            EvalContext::new(self, global, caches, scope, this_ptr.as_deref_mut());
987                        let resolved_var = resolve_var(
988                            &var.name,
989                            index.map_or(0, core::num::NonZeroUsize::get),
990                            context,
991                        );
992
993                        if orig_scope_len != scope.len() {
994                            // The scope is changed, always search from now on
995                            global.always_search_scope = true;
996                        }
997
998                        match resolved_var {
999                            // If resolved to a variable, skip it (because we cannot make it shared)
1000                            Ok(Some(_)) => continue,
1001                            Ok(None) => (),
1002                            Err(err) => return Err(err.fill_position(var.pos)),
1003                        }
1004                    }
1005
1006                    // Search the scope
1007                    let index = index
1008                        .map(|n| scope.len() - n.get())
1009                        .or_else(|| scope.search(&var.name))
1010                        .ok_or_else(|| {
1011                            Box::new(ERR::ErrorVariableNotFound(var.name.to_string(), var.pos))
1012                        })?;
1013
1014                    let val = scope.get_mut_by_index(index);
1015
1016                    if !val.is_shared() {
1017                        // Replace the variable with a shared value.
1018                        *val = val.take().into_shared();
1019                    }
1020                }
1021
1022                Ok(Dynamic::UNIT)
1023            }
1024
1025            #[allow(unreachable_patterns)]
1026            _ => unreachable!("statement cannot be evaluated: {:?}", stmt),
1027        }
1028    }
1029
1030    /// Evaluate a list of statements with no `this` pointer.
1031    /// This is commonly used to evaluate a list of statements in an [`AST`][crate::AST] or a script function body.
1032    #[inline(always)]
1033    pub(crate) fn eval_global_statements(
1034        &self,
1035        global: &mut GlobalRuntimeState,
1036        caches: &mut Caches,
1037        scope: &mut Scope,
1038        statements: &[Stmt],
1039        map_exit_to_return_value: bool,
1040    ) -> RhaiResult {
1041        self.eval_stmt_block(global, caches, scope, None, statements, false)
1042            .or_else(|err| match *err {
1043                ERR::Return(out, ..) => Ok(out),
1044                ERR::Exit(out, ..) if map_exit_to_return_value => Ok(out),
1045                ERR::LoopBreak(..) => {
1046                    unreachable!("no outer loop scope to break out of")
1047                }
1048                _ => Err(err),
1049            })
1050    }
1051}