presto-main/src/main/java/io/prestosql/sql/planner/ExpressionInterpreter.java/ExpressionInterpreter/visitFunctionCall
class ExpressionInterpreter:        @Override
        protected Object visitFunctionCall(FunctionCall node, Object context)
        {
            List<Type> argumentTypes = new ArrayList<>();
            List<Object> argumentValues = new ArrayList<>();
            for (Expression expression : node.getArguments()) {
                Object value = process(expression, context);
                Type type = type(expression);
                argumentValues.add(value);
                argumentTypes.add(type);
            }

            ResolvedFunction resolvedFunction = ResolvedFunction.fromQualifiedName(node.getName())
                    .orElseThrow(() -> new IllegalArgumentException("function call has not been resolved: " + node));
            FunctionMetadata functionMetadata = metadata.getFunctionMetadata(resolvedFunction);
            for (int i = 0; i < argumentValues.size(); i++) {
                Object value = argumentValues.get(i);
                if (value == null && !functionMetadata.getArgumentDefinitions().get(i).isNullable()) {
                    return null;
                }
            }

            // do not optimize non-deterministic functions
            if (optimize && (!functionMetadata.isDeterministic() ||
                    hasUnresolvedValue(argumentValues) ||
                    isDynamicFilter(metadata, node) ||
                    resolvedFunction.getSignature().getName().equals("fail"))) {
                verify(!node.isDistinct(), "window does not support distinct");
                verify(!node.getOrderBy().isPresent(), "window does not support order by");
                verify(!node.getFilter().isPresent(), "window does not support filter");
                return new FunctionCallBuilder(metadata)
                        .setName(node.getName())
                        .setWindow(node.getWindow())
                        .setArguments(argumentTypes, toExpressions(argumentValues, argumentTypes, node.getArguments()))
                        .build();
            }
            return functionInvoker.invoke(resolvedFunction, session, argumentValues);
        }