public Object visit(FunctionCallExpression node) {
            if (!initialCallExpressionCheck(node)) {
                return null;
            }

            Expression[] exprs = node.getParams().getExpressions();
            int length = exprs.length;
            Type[] actualTypes = new Type[length];
            for (int i=0; i<length; i++) {
                actualTypes[i] = exprs[i].getType();
            }

            Block subParam = node.getSubstitutionParam();
            Compiler compiler = mUnit.getCompiler();
            String name = node.getTarget().getName();

            // Look for Java function to call.
            name = name.replace('.', '$');

            Method m = null;
            Expression expr = node.getExpression();

            // Look for Context Method or Invoked Method
            if (m == null) {
                if (subParam != null) {
                    Type[] types = new Type[length + 1];
                    System.arraycopy(actualTypes, 0, types, 0, length);
                    types[length] =
                        new Type(openmarker.tea.runtime.Substitution.class);
                    actualTypes = types;
                }

                Method[] methods = new Method[0];
                if (expr == null) {
                    methods = compiler.getRuntimeContextMethods();
                }
                else {
                    Type exprType = expr.getType();
                    if (exprType != null) {
                        methods = exprType.getObjectClass().getMethods();
                    }
                }

                int cnt = MethodMatcher.match(methods, name, actualTypes);
                if (cnt == MethodMatcher.AMBIGUOUS) {
                    error("functioncallexpression.ambiguous", name, node);
                    return null;
                }
                else if (cnt <= 0) {
                    error("functioncallexpression.not.found", name, node);
                    return null;
                }

                m = methods[0];
            }

            if (expr instanceof TypeExpression &&
                !Modifier.isStatic(m.getModifiers())) {
                error("functioncallexpression.not.static", name, node);
            }

            if (ClassUtils.isDeprecated(m)) {
                warn("functioncallexpression.deprecated", name, node);
            }

            node.setCalledMethod(m);
            for (int i=0; i<length; i++) {
                Type converted =
                    MethodMatcher.getMethodParam(m, i, exprs[i].getType());
                exprs[i].convertTo(converted, false);
            }

            Class<?> retClass = m.getReturnType();
            java.lang.reflect.Type genericClass = m.getGenericReturnType();
            TypedElement te = m.getAnnotation(TypedElement.class);
            checkAccess(retClass, node);
            Type retType;
            if (retClass == char.class) {
                // Convert any returned char to a String.
                retType = Type.NON_NULL_STRING_TYPE;
            }
            else if (retClass == Character.class) {
                // Convert any returned Character to a String.
                retType = Type.STRING_TYPE;
            }
            else {
                retType = te != null ? new Type(retClass, genericClass, te)
                                     : new Type(retClass, genericClass);
            }
            node.setType(retType);

            if (retClass == void.class && !node.isVoidPermitted()) {
                error("functioncallexpression.function.void", node);
            }

            return null;

        }