Package org.gcontracts.generation

Source Code of org.gcontracts.generation.OldVariableGenerationUtility

/**
* Copyright (c) 2013, Andre Steingress
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
* 1.) Redistributions of source code must retain the above copyright notice, this list of conditions and the following
* disclaimer.
* 2.) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution.
* 3.) Neither the name of Andre Steingress nor the names of its contributors may be used to endorse or
* promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.gcontracts.generation;

import org.codehaus.groovy.ast.*;
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.ReturnStatement;
import org.codehaus.groovy.syntax.Token;
import org.codehaus.groovy.syntax.Types;
import org.objectweb.asm.Opcodes;

import java.util.Map;

/**
* <p>Central place where code generation for the <tt>old</tt> closure variable
* takes place.</p>
*
* @author ast
*/
public class OldVariableGenerationUtility {

    public static final String OLD_VARIABLES_METHOD = "$_gc_computeOldVariables";

    /**
     * Creates a synthetic method handling generation of the <tt>old</tt> variable map. If a super class declares
     * the same synthetic method it will be called and the results will be merged.
     *
     * @param classNode which contains postconditions, so an old variable generating method makes sense here.
     */
    public static void addOldVariableMethodNode(final ClassNode classNode)  {
        if (classNode.getDeclaredMethod(OLD_VARIABLES_METHOD, Parameter.EMPTY_ARRAY) != null) return;

        final BlockStatement methodBlockStatement = new BlockStatement();

        final MapExpression oldVariablesMap = new MapExpression();

        // create variable assignments for old variables
        for (final FieldNode fieldNode : classNode.getFields())   {
            if (fieldNode.getName().startsWith("$")) continue;

            final ClassNode fieldType = ClassHelper.getWrapper(fieldNode.getType());

            if (fieldType.getName().startsWith("java.lang") || ClassHelper.isPrimitiveType(fieldType) || fieldType.getName().startsWith("java.math") ||
                    fieldType.getName().startsWith("java.util") ||
                    fieldType.getName().startsWith("java.sql") ||
                    fieldType.getName().equals("groovy.lang.GString"||
                    fieldType.getName().equals("java.lang.String"))  {

                MethodNode cloneMethod = fieldType.getMethod("clone", Parameter.EMPTY_ARRAY);
                // if a clone classNode is available, the value is cloned
                if (cloneMethod != null && fieldType.implementsInterface(ClassHelper.make("java.lang.Cloneable")))  {
                    VariableExpression oldVariable = new VariableExpression("$old$" + fieldNode.getName(), fieldNode.getType());
                    oldVariable.setAccessedVariable(oldVariable);

                    final MethodCallExpression methodCall = new MethodCallExpression(new FieldExpression(fieldNode), "clone", ArgumentListExpression.EMPTY_ARGUMENTS);
                    // return null if field is null
                    methodCall.setSafe(true);

                    ExpressionStatement oldVariableAssignment = new ExpressionStatement(
                        new DeclarationExpression(oldVariable,
                        Token.newSymbol(Types.ASSIGN, -1, -1),
                                methodCall));

                    methodBlockStatement.addStatement(oldVariableAssignment);
                    oldVariablesMap.addMapEntryExpression(new MapEntryExpression(new ConstantExpression(oldVariable.getName().substring("$old$".length())), oldVariable));

                } else if (ClassHelper.isPrimitiveType(fieldType)
                        || ClassHelper.isNumberType(fieldType)
                        || fieldType.getName().startsWith("java.math")
                        || fieldType.getName().equals("groovy.lang.GString")
                        || fieldType.getName().equals("java.lang.String")) {

                    VariableExpression oldVariable = new VariableExpression("$old$" + fieldNode.getName(), fieldNode.getType());
                    oldVariable.setAccessedVariable(oldVariable);

                    ExpressionStatement oldVariableAssignment = new ExpressionStatement(
                        new DeclarationExpression(oldVariable,
                        Token.newSymbol(Types.ASSIGN, -1, -1),
                        new FieldExpression(fieldNode)));

                    methodBlockStatement.addStatement(oldVariableAssignment);
                    oldVariablesMap.addMapEntryExpression(new MapEntryExpression(new ConstantExpression(oldVariable.getName().substring("$old$".length())), oldVariable));
                }
            }
        }

        VariableExpression oldVariable = new VariableExpression("old", new ClassNode(Map.class));
        oldVariable.setAccessedVariable(oldVariable);

        ExpressionStatement oldVariabeStatement = new ExpressionStatement(
                new DeclarationExpression(oldVariable,
                        Token.newSymbol(Types.ASSIGN, -1, -1),
                        oldVariablesMap));

        methodBlockStatement.addStatement(oldVariabeStatement);

        VariableExpression mergedOldVariables = null;

        // let's ask the super class for old variables...
        if (classNode.getSuperClass() != null && classNode.getSuperClass().getMethod(OLD_VARIABLES_METHOD, Parameter.EMPTY_ARRAY) != null)  {
            mergedOldVariables = new VariableExpression("mergedOldVariables", new ClassNode(Map.class));
            mergedOldVariables.setAccessedVariable(mergedOldVariables);

            ExpressionStatement mergedOldVariablesStatement = new ExpressionStatement(
                new DeclarationExpression(mergedOldVariables,
                        Token.newSymbol(Types.ASSIGN, -1, -1),
                        new MethodCallExpression(oldVariable, "plus", new ArgumentListExpression(new MethodCallExpression(VariableExpression.SUPER_EXPRESSION, OLD_VARIABLES_METHOD, ArgumentListExpression.EMPTY_ARGUMENTS)))));


            methodBlockStatement.addStatement(mergedOldVariablesStatement);
        }

        methodBlockStatement.addStatement(new ReturnStatement(mergedOldVariables != null ? mergedOldVariables : oldVariable));

        final MethodNode preconditionMethodNode = classNode.addMethod(OLD_VARIABLES_METHOD, Opcodes.ACC_PROTECTED, new ClassNode(Map.class), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, methodBlockStatement);
        preconditionMethodNode.setSynthetic(true);

    }
}
TOP

Related Classes of org.gcontracts.generation.OldVariableGenerationUtility

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.