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

Code before:
    type Output = Value;

    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
        let span = self.span();
        let callee = self.callee();
        let in_math = in_math(callee);
        let callee_span = callee.span();
        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_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.
                    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(),
                }
            } 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>();

Review comment (suggestion):
Paging @laurmaedje : I think this `args.span = span;` was at odds with the original `.spanned(span)`, right? It does seem that `.spanned(span)` only changes the span if there wasn't already one, while `.span = span` will always change it. I think `.spanned(span)` is enough here, since `.eval` creates an `Args` with a detached span, so this should be ok to be removed, correct? (Just wanted to make sure we're doing the right thing here)

Code after:
impl Eval for ast::FuncCall<'_> {
    type Output = Value;

    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
        let span = self.span();
        let callee = self.callee();
        let in_math = in_math(callee);
        let callee_span = callee.span();
        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)
    }
}