Repository: typst/typst
File: crates/typst/src/eval/call.rs (Rust)

Code before:
        let trailing_comma = args.trailing_comma();

        if !vm.engine.route.within(Route::MAX_CALL_DEPTH) {
            bail!(span, "maximum function call depth exceeded");
        }

        // Try to evaluate as a call to an associated function or field.
        let (callee, args) = if let ast::Expr::FieldAccess(access) = callee {
            let target_expr = access.target();
            let field = access.field();
            let mut args = args.eval(vm)?.spanned(span);

            // Evaluate the target expression.
            let target = if is_mutating_method(&field) {
                match target_expr.access(vm)? {
                    // Only arrays and dictionaries have mutable methods.
                    Value::Array(_) | Value::Dict(_) => {
                        let point = || Tracepoint::Call(Some(field.get().clone()));
                        return call_method_mut(
                            target_expr.access(vm)?,
                            &field,
                            args,
                            span,
                        )
                        .trace(vm.world(), point, span);
                    }
                    target => target.clone(),
                }
            } else {
                target_expr.eval(vm)?
            };

            // Handle plugins.
            if let Value::Plugin(plugin) = &target {
                let bytes = args.all::<Bytes>()?;
                args.finish()?;
                return Ok(plugin.call(&field, bytes).at(span)?.into_value());
            }

            eval_callee_args(target, target_expr.span(), field, args)?
        } else {
            (callee.eval(vm)?, args.eval(vm)?.spanned(span))
        };

        let func_result = callee.clone().cast::<Func>();
        if in_math && func_result.is_err() {
            return wrap_args_in_math(callee, callee_span, args, trailing_comma);
        }

        let func = func_result
            .map_err(|err| hint_if_shadowed_std(vm, &self.callee(), err))

Review comment (suggestion):
We should perhaps not access twice, if possible:

```suggestion
                match target_expr.access(vm)? {
                    // Only arrays and dictionaries have mutable methods.
                    target@(Value::Array(_) | Value::Dict(_)) => {
                        let point = || Tracepoint::Call(Some(field.get().clone()));
                        return call_method_mut(
                            target,
                            &field,
                            args,
                            span,
                        )
                        .trace(vm.world(), point, span);
                    }
                    target => target.clone(),
                }
```

Code after:
        let args = self.args();
        let trailing_comma = args.trailing_comma();

        if !vm.engine.route.within(Route::MAX_CALL_DEPTH) {
            bail!(span, "maximum function call depth exceeded");
        }

        // Try to evaluate as a call to an associated function or field.
        let (callee, args) = if let ast::Expr::FieldAccess(access) = callee {
            let target = access.target();
            let field = access.field();
            match eval_field_call(target, field, args, span, vm)? {
                FieldCall::Normal(callee, args) => (callee, args),
                FieldCall::Resolved(value) => return Ok(value),
            }
        } else {
            // Function call order: we evaluate the callee before the arguments.
            (callee.eval(vm)?, args.eval(vm)?.spanned(span))
        };

        let func_result = callee.clone().cast::<Func>();
        if in_math && func_result.is_err() {
            return wrap_args_in_math(callee, callee_span, args, trailing_comma);
        }

        let func = func_result
            .map_err(|err| hint_if_shadowed_std(vm, &self.callee(), err))
            .at(callee_span)?;

        let point = || Tracepoint::Call(func.name().map(Into::into));
        let f = || {
            func.call(&mut vm.engine, vm.context, args)
                .trace(vm.world(), point, span)
        };

        // Stacker is broken on WASM.
        #[cfg(target_arch = "wasm32")]
        return f();

        #[cfg(not(target_arch = "wasm32"))]
        stacker::maybe_grow(32 * 1024, 2 * 1024 * 1024, f)
    }
}

impl Eval for ast::Args<'_> {
    type Output = Args;

    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
        let mut items = EcoVec::with_capacity(self.items().count());

        for arg in self.items() {