private void generateCallExpression(final CallExpression node) {
            // generate expression if applicable
            final Expression expr = node.getExpression();
            if (expr != null) {
                generate(expr);
            }

            // build null-safe expression if applicable
            NullSafeCallback callback = new NullSafeCallback() {
                public Type execute() {
                    Statement init = node.getInitializer();
                    Statement subParam = node.getSubstitutionParam();
                    Expression[] exprs = node.getParams().getExpressions();

                    int blockNum;
                    if (subParam != null) {
                        blockNum = mCaseNodes.size() - 1;
                        mBuilder.loadThis();
                        mBuilder.loadConstant(blockNum + 1);
                        mBuilder.storeField(mBlockId.getName(), TypeDesc.INT);
                        mCaseNodes.add(subParam);
                        if (node instanceof FunctionCallExpression) {
                            Method call =
                                ((FunctionCallExpression) node).getCalledMethod();
                            mCallerToSubNoList.add("__block_"+ call.getName());
                        }
                        if (node instanceof TemplateCallExpression) {
                            CompilationUnit unit =
                                ((TemplateCallExpression) node).getCalledTemplate();
                            mCallerToSubNoList.add("__block__"+ unit.getName());
                        }
                    }
                    else {
                        blockNum = 0;
                    }

                    // Inject pre-call profiling bytecode.
                    TypeDesc methodObserverType =
                        TypeDesc.forClass(MergedClass.InvocationEventObserver.class);
                    LocalVariable retVal = null;
                    String calleeName = null;
                    Type returnType = null;
                    TypeDesc returnTypeDesc = null;

                    boolean profilingEnabled = isProfilingEnabled();
                    if (profilingEnabled) {
                        if (mStartTime == null)
                            mStartTime = mBuilder.createLocalVariable("startTime",
                                TypeDesc.forClass(long.class));
                        mBuilder.invokeStatic(mContextParam.getVariable().getType().getObjectClass().getName(),
                            "getInvocationObserver", methodObserverType);
                        mBuilder.invokeInterface(methodObserverType.getFullName(), "currentTime",
                           TypeDesc.forClass(long.class));
                        mBuilder.storeLocal(mStartTime);
                    }

                    if (node instanceof FunctionCallExpression) {
                        FunctionCallExpression function =
                            (FunctionCallExpression) node;
                        Method call = function.getCalledMethod();

                        if (expr == null &&
                            !Modifier.isStatic(call.getModifiers())) {

                            // Push instance of Context onto stack.
                            generateContext();
                        }

                        // check if var-arg to inject params as array
                        Class<?>[] params = call.getParameterTypes();
                        for (int i = 0; i < exprs.length; i++) {
                            if (call.isVarArgs() && i == params.length - 1) {
                                Class<?> type =
                                    exprs[i].getType().getNaturalClass();
                                if (!params[i].isAssignableFrom(type)) {
                                    // new array, generate exprs, store to array, load array
                                    mBuilder.loadConstant(exprs.length);

                                    TypeDesc desc = makeDesc(params[i]);
                                    mBuilder.newObject(desc, 1);
                                    for (int idx = 0, j = i; j < exprs.length; j++, idx++) {
                                        mBuilder.dup();
                                        mBuilder.loadConstant(idx);
                                        generate(exprs[j]);
                                        mBuilder.storeToArray(desc.getComponentType());
                                    }

                                    break;
                                }
                            }

                            generate(exprs[i]);
                        }

                        if (call.isVarArgs() && exprs.length < params.length) {
                            // generate empty array for var arg
                            mBuilder.loadConstant(0);
                            mBuilder.newObject(makeDesc(params[params.length - 1]), 1);
                        }

                        if (subParam != null) {
                            // Put this onto the stack as a substitution parameter.
                            mBuilder.loadThis();
                        }

                        // Generate init right before the call.
                        if (init != null) {
                            generate(init);
                        }

                        if (call.getReturnType() != null) {
                            returnType = new Type
                            (
                                call.getReturnType(),
                                call.getGenericReturnType()
                            );

                            returnTypeDesc = makeDesc(returnType);
                            retVal = mBuilder.createLocalVariable("retVal", returnTypeDesc);
                        }

                        mBuilder.invoke(call);

                        calleeName = call.getName();
                    }
                    else if (node instanceof TemplateCallExpression) {
                        CompilationUnit unit =
                            ((TemplateCallExpression) node).getCalledTemplate();

                        // Push instance of Context onto stack as first parameter.
                        if (expr == null) {
                            generateContext();
                        }

                        for (int i=0; i<exprs.length; i++) {
                            generate(exprs[i]);
                        }

                        // Generate init right before the call.
                        if (init != null) {
                            generate(init);
                        }

                        String className = unit.getTargetPackage();
                        if (className == null) {
                            className = unit.getName();
                        }
                        else {
                            className = className + '.' + unit.getName();
                        }

                        Template tree = unit.getParseTree();

                        Variable[] formals = tree.getParams();
                        int length = formals.length;

                        TypeDesc[] params;
                        if (subParam == null) {
                            params = new TypeDesc[length + 1];
                        }
                        else {
                            params = new TypeDesc[length + 2];
                            params[params.length - 1] = makeDesc(Substitution.class);
                            // Put this onto the stack as a substitution parameter.
                            mBuilder.loadThis();
                        }

                        params[0] = makeDesc(unit.getRuntimeContext());

                        for (int i=0; i<length; i++) {
                            params[i + 1] = makeDesc(formals[i]);
                        }

                        if (tree.getReturnType() != null) {
                            returnType = tree.getReturnType();
                            returnTypeDesc = makeDesc(tree.getReturnType());
                            retVal = mBuilder.createLocalVariable("retVal", returnTypeDesc);
                        }

                        calleeName = "_" + unit.getName();

                        mBuilder.invokeStatic(className, EXECUTE_METHOD_NAME, returnTypeDesc, params);
                    }

                    if (returnTypeDesc != null && ! TypeDesc.VOID.equals(returnTypeDesc))
                        mBuilder.storeLocal(retVal);

                    // Inject post-call profiling bytecode.
                    if (profilingEnabled) {
                        mBuilder.invokeStatic(mContextParam.getVariable().getType().getObjectClass().getName(),
                            "getInvocationObserver", methodObserverType);
                        mBuilder.loadConstant(mUnit.getName());
                        mBuilder.loadConstant(calleeName);
                        mBuilder.invokeStatic(mContextParam.getVariable().getType().getObjectClass().getName(),
                            "getInvocationObserver", methodObserverType);
                        mBuilder.invokeInterface(methodObserverType.getFullName(), "currentTime",
                            TypeDesc.forClass(long.class));
                        mBuilder.loadLocal(mStartTime);
                        mBuilder.math(Opcode.LSUB);
                        mBuilder.invokeInterface(methodObserverType.getFullName(), "invokedEvent", null,
                            new TypeDesc[] { TypeDesc.forClass(String.class), TypeDesc.forClass(String.class),
                                TypeDesc.forClass(long.class) });
                    }

                    if (returnTypeDesc != null && ! TypeDesc.VOID.equals(returnTypeDesc)) {
                        mBuilder.loadLocal(retVal);
                    }

                    if (subParam != null) {
                        mBuilder.loadThis();
                        mBuilder.loadConstant(blockNum);
                        mBuilder.storeField(mBlockId.getName(), TypeDesc.INT);
                    }

                    return returnType;
                }
            };

            // generate null-safe if provided
            if (expr != null) {
                generateNullSafe(node, expr, callback);
            }

            // otherwise, invoke callback by itself
            else {
                callback.execute();
            }
        }