Package org.grails.compiler.web.gsp

Source Code of org.grails.compiler.web.gsp.GroovyPageOptimizerVisitor

/*
* Copyright 2011 SpringSource
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.grails.compiler.web.gsp;

import groovy.lang.Closure;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.CodeVisitorSupport;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.expr.*;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.grails.web.pages.GroovyPage;
import org.grails.buffer.GrailsPrintWriter;
import org.codehaus.groovy.syntax.Token;
import org.codehaus.groovy.syntax.Types;

import java.util.List;
import java.util.Stack;

/**
* Scan MethodCallExpression in GSP to convert callsite calls into static calls (printHtmlPart ...)
*
* @author Stephane Maldini
* @since 2.0
*/
class GroovyPageOptimizerVisitor extends CodeVisitorSupport {

    private static final ClassNode groovyPageClassNode = new ClassNode(GroovyPage.class);
    private static final MethodNode writerMethodNode = new ClassNode(GrailsPrintWriter.class).getMethod("print",
            new Parameter[]{new Parameter(new ClassNode(Object.class), "obj")});

    private static final String THIS_RECEIVER = "this";
    private static final String THISOBJECT = "thisObject";
    private static final String OUT_RECEIVER = "out";
    private static final String PRINT_METHOD = "print";
    private static final String EXPRESSIONOUT_RECEIVER = "expressionOut";

    private Stack<ClosureExpression> innerClosures = new Stack<ClosureExpression>();
    private ClassNode targetGroovyPageNode;

    private DeclarationExpression thisObjectDeclaration;
    private VariableExpression thisObjectVariable;

    public GroovyPageOptimizerVisitor(ClassNode targetGroovyPage) {
        this.targetGroovyPageNode = targetGroovyPage;

        MethodCallExpression thisObjectMethodCall = new MethodCallExpression(new VariableExpression(THIS_RECEIVER), "getThisObject", MethodCallExpression.NO_ARGUMENTS);
        thisObjectMethodCall.setMethodTarget(new ClassNode(Closure.class).getMethods("getThisObject").get(0));

        thisObjectVariable = new VariableExpression(THISOBJECT, targetGroovyPageNode);

        thisObjectDeclaration = new DeclarationExpression(
                    thisObjectVariable
                    ,Token.newSymbol(Types.EQUALS, 0, 0)
                    ,thisObjectMethodCall);
    }

// TODO: Research why http://jira.grails.org/browse/GRAILS-8679 happens with this enabled. See ElvisAndClosureGroovyPageTests
//    @Override
//    public void visitClosureExpression(ClosureExpression expression) {
//        innerClosures.push(expression);
//        introduceThisObjectVariable(expression);
//        super.visitClosureExpression(expression);
//        innerClosures.pop();
//    }

    @SuppressWarnings("unused")
    private void introduceThisObjectVariable(ClosureExpression closureExpression) {
        if (closureExpression.getCode() instanceof BlockStatement) {
            List<Statement> oldBlock = ((BlockStatement)closureExpression.getCode()).getStatements();
            BlockStatement newBlock = new BlockStatement();

            newBlock.addStatement(new ExpressionStatement(thisObjectDeclaration));
            newBlock.addStatements(oldBlock);

            closureExpression.setCode(newBlock);
        }
    }

    @Override
    public void visitMethodCallExpression(MethodCallExpression call) {

        if (isCallFromGroovyPageClass(call)) {
            // TODO: Research why http://jira.grails.org/browse/GRAILS-8679 happens with this enabled. See ElvisAndClosureGroovyPageTests
            //proceedCallFromGroovyPageClass(call);
        } else if (isCallFromOutOrCodecOut(call)) {
            proceedCallFromOutOrCodecOut(call);
        }

        super.visitMethodCallExpression(call);
    }

    private void proceedCallFromOutOrCodecOut(MethodCallExpression call) {
        call.setMethodTarget(writerMethodNode);
    }

    private boolean isCallFromOutOrCodecOut(MethodCallExpression expression) {
        return (expression.getObjectExpression().getText().equals(OUT_RECEIVER)
                || expression.getObjectExpression().getText().equals(EXPRESSIONOUT_RECEIVER))
                && expression.getMethodAsString().equals(PRINT_METHOD);
    }

    @SuppressWarnings("unused")
    private void proceedCallFromGroovyPageClass(MethodCallExpression call) {
        List<MethodNode> methodNodeList = groovyPageClassNode.getMethods(call.getMethodAsString());

        if (methodNodeList.size() == 1) {
            call.setMethodTarget(methodNodeList.get(0));
            changeThisObjectExpressionIfInnerClosure(call);
        } else if (methodNodeList.size() > 1 && call.getArguments() instanceof ArgumentListExpression) {
            //Special case for invokeTag
            ArgumentListExpression argsExpr = ((ArgumentListExpression) call.getArguments());

            for (MethodNode methodNode : methodNodeList) {
                //No need for deep analysis as GroovyPage doesn't have multiple signatures for same args number
                if (methodNode.getParameters().length == argsExpr.getExpressions().size()) {
                    call.setMethodTarget(methodNode);
                    changeThisObjectExpressionIfInnerClosure(call);
                    break;
                }
            }
        }
    }

    private void changeThisObjectExpressionIfInnerClosure(MethodCallExpression call) {
        if (!innerClosures.isEmpty()) {
            call.setObjectExpression(thisObjectVariable);
        }
    }

    private boolean isCallFromGroovyPageClass(MethodCallExpression expression) {
        return expression.getObjectExpression().getText().equals(THIS_RECEIVER)
                && !groovyPageClassNode.getMethods(expression.getMethodAsString()).isEmpty();
    }
}
TOP

Related Classes of org.grails.compiler.web.gsp.GroovyPageOptimizerVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.