org.sosy_lab.cpachecker.cfa.ast.c.CInitializers.java Source code

Java tutorial

Introduction

Here is the source code for org.sosy_lab.cpachecker.cfa.ast.c.CInitializers.java

Source

/*
 *  CPAchecker is a tool for configurable software verification.
 *  This file is part of CPAchecker.
 *
 *  Copyright (C) 2007-2014  Dirk Beyer
 *  All rights reserved.
 *
 *  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.
 *
 *
 *  CPAchecker web page:
 *    http://cpachecker.sosy-lab.org
 */
package org.sosy_lab.cpachecker.cfa.ast.c;

import static com.google.common.collect.FluentIterable.from;

import java.math.BigInteger;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.sosy_lab.cpachecker.cfa.ast.FileLocation;
import org.sosy_lab.cpachecker.cfa.model.CFAEdge;
import org.sosy_lab.cpachecker.cfa.types.c.CArrayType;
import org.sosy_lab.cpachecker.cfa.types.c.CBasicType;
import org.sosy_lab.cpachecker.cfa.types.c.CComplexType.ComplexTypeKind;
import org.sosy_lab.cpachecker.cfa.types.c.CCompositeType;
import org.sosy_lab.cpachecker.cfa.types.c.CCompositeType.CCompositeTypeMemberDeclaration;
import org.sosy_lab.cpachecker.cfa.types.c.CElaboratedType;
import org.sosy_lab.cpachecker.cfa.types.c.CNumericTypes;
import org.sosy_lab.cpachecker.cfa.types.c.CPointerType;
import org.sosy_lab.cpachecker.cfa.types.c.CSimpleType;
import org.sosy_lab.cpachecker.cfa.types.c.CType;
import org.sosy_lab.cpachecker.exceptions.UnrecognizedCCodeException;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.ContiguousSet;
import com.google.common.collect.DiscreteDomain;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Range;

/**
 * Utility class for initializer-related tasks.
 */
public final class CInitializers {

    private CInitializers() {
    }

    /**
     * Take a variable declaration and create a list of assignment statements
     * that assign the value(s) of the initializer to the declared variable,
     * including cases with complex initializers for structs and arrays.
     *
     * Note that there is currently one unhandled case:
     * In C, for structs and arrays where a brace-delimited initializer list is present,
     * all fields/elements that are not explicitly initialized
     * are initialized with their default value.
     * However, this method does not return any assignments for these default values.
     *
     * Example:
     * <code>
     * struct {
     *   int i;
     *   int a[3];
     * } s = { 1, 2, 3 };
     * </code>
     * will be converted into
     * <code>
     * s.i = 1;
     * s.a[0] = 2;
     * s.a[1] = 3;
     * </code>
     * (The assignment s.a[2] = 0 is missing as explained above.)
     *
     * @param decl The variable declaration.
     * @param edge The current CFA edge.
     * @return A (possibly empty) list of assignment statements.
     * @throws UnrecognizedCCodeException
     */
    public static List<CExpressionAssignmentStatement> convertToAssignments(CVariableDeclaration decl, CFAEdge edge)
            throws UnrecognizedCCodeException {

        CInitializer init = decl.getInitializer();
        if (init == null) {
            return ImmutableList.of();
        }

        CLeftHandSide lhs = new CIdExpression(decl.getFileLocation(), decl);

        if (init instanceof CInitializerExpression) {
            CExpression initExp = ((CInitializerExpression) init).getExpression();
            // Create a regular assignment
            CExpressionAssignmentStatement assignment = new CExpressionAssignmentStatement(decl.getFileLocation(),
                    lhs, initExp);
            return ImmutableList.of(assignment);

        } else if (init instanceof CInitializerList) {
            return handleInitializerList(lhs, (CInitializerList) init, decl.getFileLocation(), edge);

        } else {
            throw new UnrecognizedCCodeException("Unknown initializer type", edge, init);
        }
    }

    /**
     * Handle a brace-delimited initializer list as defined in  6.7.9 of the C standard.
     * @param currentObject The "current object".
     * @param initializerList The initializer list for the "current object".
     */
    private static List<CExpressionAssignmentStatement> handleInitializerList(final CExpression currentObject,
            final CInitializerList initializerList, final FileLocation loc, final CFAEdge edge)
            throws UnrecognizedCCodeException {

        // The term "current object" is defined in the C standard, 6.7.9 (17)
        // The initializer list is the initializer for the "current object"
        // and its subobjects.
        // One call of this method is responsible for initializing the "current object"
        // and its subobjects completely.
        // It calls itself recursively if it encounters nested brace-enclosed
        // initializer lists, because inside such lists, the "current object" is different.

        // We keep two stacks here.
        // - currentSubobjects is the stack of expressions to access
        //   the current subobject and its outer object up to the "current object"
        //   Example: [s.t.f, s.t, s]
        // - nextSubobjects is the stack of iterators giving the next subobjects
        //   to handle after the current subobject was handled
        //   (i.e., the neighboring fields of the current subobjects)
        // Both these stacks are modified by other methods.

        Deque<CExpression> currentSubobjects = new ArrayDeque<>(2);
        Deque<Iterator<CExpression>> nextSubobjects = new ArrayDeque<>(2);

        { // For starting, we go to the first very subobject of the "current object".
          // We cannot go to the first suboject at the deepest nesting level
          // as findFirstSubobjectWithType does in case the first initializer value
          // is a nested brace-delimited initializer list.
            currentSubobjects.push(currentObject);
            CType currentType = currentObject.getExpressionType().getCanonicalType();
            boolean successful;

            if (currentType instanceof CCompositeType
                    && ((CCompositeType) currentType).getKind() != ComplexTypeKind.ENUM) {
                successful = handleInitializerForCompositeType(currentObject, Optional.<String>absent(),
                        (CCompositeType) currentType, currentSubobjects, nextSubobjects, loc, edge, null);

            } else if (currentType instanceof CArrayType) {
                successful = handleInitializerForArray(currentObject, 0L, (CArrayType) currentType,
                        currentSubobjects, nextSubobjects, loc, edge, null);
            } else if (currentType instanceof CElaboratedType) {
                throw new UnrecognizedCCodeException(
                        "Unexpected initializer for " + currentType + " that is not fully defined", edge,
                        initializerList);
            } else {
                throw new UnrecognizedCCodeException(
                        "Unexpected initializer list for " + currentObject + " with type " + currentType, edge,
                        initializerList);
            }

            if (!successful) {
                // struct or array was empty, no initializing needed
                if (!initializerList.getInitializers().isEmpty()) {
                    throw new UnrecognizedCCodeException("Too many values in initializer list", edge,
                            initializerList);
                }
                return ImmutableList.of();
            }
        }

        List<CExpressionAssignmentStatement> result = new ArrayList<>(initializerList.getInitializers().size());
        for (CInitializer init : initializerList.getInitializers()) {

            if (init instanceof CDesignatedInitializer) {
                // first, this resets everything except the "current object"
                findDesignatedSubobject(((CDesignatedInitializer) init).getDesignators(), currentObject,
                        currentSubobjects, nextSubobjects, loc, edge);

                // now analyze the real initializer part
                init = ((CDesignatedInitializer) init).getRightHandSide();
                if (init instanceof CDesignatedInitializer) {
                    throw new UnrecognizedCCodeException("Too complex struct initializer", edge, initializerList);
                }
            }

            if (currentSubobjects.isEmpty()) {
                throw new UnrecognizedCCodeException("Too many values in initializer list", edge, initializerList);
            }

            if (init instanceof CInitializerList) {
                // nested bracketed initializer, handle recursively
                // (the content of the brackets has the current subobject as the "current object"

                final CExpression currentSubobject = currentSubobjects.pop();
                result.addAll(handleInitializerList(currentSubobject, (CInitializerList) init, loc, edge));

            } else if (init instanceof CInitializerExpression) {
                // simple expression,
                CExpression initExp = ((CInitializerExpression) init).getExpression();
                CType initType = initExp.getExpressionType().getCanonicalType();

                // This applies to the first field/element of the current subobject,
                // which might be on a deeper nesting level than we currently are,
                // so we build the stacks if necessary.
                findFirstSubobjectWithType(initType, currentSubobjects, nextSubobjects, loc, edge);

                assert currentSubobjects.peek() instanceof CLeftHandSide : "Object hast to be a LeftHandSide";
                final CLeftHandSide currentSubobject = (CLeftHandSide) currentSubobjects.pop();

                // Do a regular assignment
                CExpressionAssignmentStatement assignment = new CExpressionAssignmentStatement(loc,
                        currentSubobject, initExp);

                result.add(assignment);

            } else {
                throw new UnrecognizedCCodeException("Unknown initializer type", edge, init);
            }

            // Prepare the stacks for the next iteration.
            // Cleanup the iterators stack.
            while (!nextSubobjects.isEmpty() && !nextSubobjects.peek().hasNext()) {
                nextSubobjects.pop();
                currentSubobjects.pop();
            }

            // Fetch the next subobject to handle.
            if (!nextSubobjects.isEmpty()) {
                currentSubobjects.push(nextSubobjects.peek().next());
            }
        }

        return result;
    }

    /**
     * Given a designator list of an initializer for an aggregate type,
     * this method builds the two stacks for the subobjects and the iterators
     * such that the designated field/element is the next that will be accessed.
     * Prior to that, both stacks are reset.
     * @param designators A list of designators (e.g. ".f[2][1-4].t")
     * @param currentObject the "current object" with which this whole chain of initializers is associated
     * @param currentSubobjects as in {@link #handleInitializerList(CExpression, CInitializerList, FileLocation, CFAEdge)}
     * @param nextSubobjects as in {@link #handleInitializerList(CExpression, CInitializerList, FileLocation, CFAEdge)}
     */
    private static void findDesignatedSubobject(final List<CDesignator> designators,
            final CExpression currentObject, final Deque<CExpression> currentSubobjects,
            final Deque<Iterator<CExpression>> nextSubobjects, final FileLocation loc, final CFAEdge edge)
            throws UnrecognizedCCodeException {

        currentSubobjects.clear();
        nextSubobjects.clear();
        currentSubobjects.push(currentObject);

        // We just iterate through the designators and push onto both stacks.
        for (CDesignator designator : designators) {
            final CExpression currentSubobject = currentSubobjects.peek();
            final CType currentType = currentSubobject.getExpressionType().getCanonicalType();
            boolean successful;

            if (designator instanceof CFieldDesignator) {
                String fieldName = ((CFieldDesignator) designator).getFieldName();
                if (!(currentType instanceof CCompositeType)
                        || ((CCompositeType) currentType).getKind() == ComplexTypeKind.ENUM) {
                    throw new UnrecognizedCCodeException("Designated field initializer for non-struct type", edge,
                            designator);
                }

                successful = handleInitializerForCompositeType(currentSubobject, Optional.of(fieldName),
                        (CCompositeType) currentType, currentSubobjects, nextSubobjects, loc, edge, designator);

            } else if (designator instanceof CArrayDesignator) {
                if (!(currentType instanceof CArrayType)) {
                    throw new UnrecognizedCCodeException("Designated array initializer for non-array type", edge,
                            designator);
                }

                CArrayType arrayType = (CArrayType) currentType;
                CExpression indexExp = ((CArrayDesignator) designator).getSubscriptExpression();

                if (!(indexExp instanceof CIntegerLiteralExpression)) {
                    throw new UnrecognizedCCodeException("Cannot evaluate expression as array designator", edge,
                            designator);
                }

                BigInteger index = ((CIntegerLiteralExpression) indexExp).getValue();
                if (!BigInteger.valueOf(index.longValue()).equals(index)) {
                    throw new UnrecognizedCCodeException("Array designator is too large to initialize explicitly",
                            edge, designator);
                }

                successful = handleInitializerForArray(currentSubobject, index.longValue(), arrayType,
                        currentSubobjects, nextSubobjects, loc, edge, designator);

            } else if (designator instanceof CArrayRangeDesignator) {
                if (!(currentType instanceof CArrayType)) {
                    throw new UnrecognizedCCodeException("Designated array initializer for non-array type", edge,
                            designator);
                }

                CArrayType arrayType = (CArrayType) currentType;
                CExpression floorExp = ((CArrayRangeDesignator) designator).getFloorExpression();
                CExpression ceilExp = ((CArrayRangeDesignator) designator).getCeilExpression();

                if (!(floorExp instanceof CIntegerLiteralExpression)
                        || !(ceilExp instanceof CIntegerLiteralExpression)) {
                    throw new UnrecognizedCCodeException("Cannot evaluate expression as array range designator",
                            edge, designator);
                }

                BigInteger indexBottom = ((CIntegerLiteralExpression) floorExp).getValue();
                BigInteger indexTop = ((CIntegerLiteralExpression) ceilExp).getValue();
                if (!BigInteger.valueOf(indexBottom.longValue()).equals(indexBottom)
                        || !BigInteger.valueOf(indexTop.longValue()).equals(indexTop)) {
                    throw new UnrecognizedCCodeException(
                            "Array range designator is too large to initialize explicitly", edge, designator);
                }

                successful = true;
                for (long index = indexBottom.longValue(); index < indexTop.longValue() && successful; index++) {
                    successful = handleInitializerForArray(currentSubobject, index, arrayType, currentSubobjects,
                            nextSubobjects, loc, edge, designator);
                }

            } else {
                throw new UnrecognizedCCodeException("Unrecognized initializer designator", edge, designator);
            }

            if (!successful) {
                throw new UnrecognizedCCodeException("Empty struct or array is not supported as field", edge,
                        currentSubobject);
            }
        }
    }

    // safe because Iterator is covariant
    @SuppressWarnings("unchecked")
    private static <T> Iterator<T> safeCast(Iterator<? extends T> it) {
        return (Iterator<T>) it;
    }

    /**
     * Find the first subobject inside the current subobject that may be
     * initialized with a value of a given type.
     * Usually this method just enters nested structs and arrays until
     * it finds the first field/element that is not of an aggegrate type.
     * However, if the given type is for example a struct,
     * and it encounters a field/element of this type, it does not enter
     * this subobject.
     *
     * This method only pushes objects on the two stacks until their position is correct.
     *
     * @param targetType The type to search.
     * @param currentSubobjects as in {@link #handleInitializerList(CExpression, CInitializerList, FileLocation, CFAEdge)}
     * @param nextSubobjects as in {@link #handleInitializerList(CExpression, CInitializerList, FileLocation, CFAEdge)}
     * @param loc
     * @param edge
     * @throws UnrecognizedCCodeException
     */
    private static void findFirstSubobjectWithType(final CType targetType,
            final Deque<CExpression> currentSubobjects, final Deque<Iterator<CExpression>> nextSubobjects,
            final FileLocation loc, CFAEdge edge) throws UnrecognizedCCodeException {

        while (true) {
            final CExpression currentSubobject = currentSubobjects.peek();
            final CType currentType = currentSubobject.getExpressionType().getCanonicalType();

            if (targetType.equals(currentType)) {
                break;
            }

            // String literals may be used to initialize char arrays.
            // They have a type of (const char)*.
            if (targetType.equals(CPointerType.POINTER_TO_CONST_CHAR) && currentType instanceof CArrayType) {
                CType currentElementType = ((CArrayType) currentType).getType();
                if (currentElementType instanceof CSimpleType
                        && ((CSimpleType) currentElementType).getType() == CBasicType.CHAR) {
                    break;
                }
            }
            boolean successful;

            if (currentType instanceof CCompositeType
                    && ((CCompositeType) currentType).getKind() != ComplexTypeKind.ENUM) {
                successful = handleInitializerForCompositeType(currentSubobject, Optional.<String>absent(),
                        (CCompositeType) currentType, currentSubobjects, nextSubobjects, loc, edge, null);

            } else if (currentType instanceof CArrayType) {
                successful = handleInitializerForArray(currentSubobject, 0L, (CArrayType) currentType,
                        currentSubobjects, nextSubobjects, loc, edge, null);

            } else {
                // any other type is not an aggregate type
                break;
            }

            if (!successful) {
                throw new UnrecognizedCCodeException("Empty struct or array is not supported as field", edge,
                        currentSubobject);
            }
        }
    }

    /**
     * Handle the case when the current subobject that will be initialized next
     * is a composite type (struct or union).
     * This method only prepares the two stacks by pushing one object on both
     * of them (for the next field to be initialized, and the iterator for the
     * remainder of the fields).
     * If a field name is given, this method ignores (i.e., jumps over)
     * all fields that appear _before_ that given field.
     * @param currentSubobject The struct/union to be initialized
     * @param startingFieldName The optional field name to look for
     * @param structType The type of currentSubobject
     * @param currentSubobjects as in {@link #handleInitializerList(CExpression, CInitializerList, FileLocation, CFAEdge)}
     * @param nextSubobjects as in {@link #handleInitializerList(CExpression, CInitializerList, FileLocation, CFAEdge)}
     */
    private static boolean handleInitializerForCompositeType(final CExpression currentSubobject,
            final Optional<String> startingFieldName, final CCompositeType structType,
            final Deque<CExpression> currentSubobjects, final Deque<Iterator<CExpression>> nextSubobjects,
            final FileLocation loc, final CFAEdge edge, final CDesignator designator)
            throws UnrecognizedCCodeException {

        Iterator<CFieldReference> fields = from(structType.getMembers())
                .transform(new Function<CCompositeTypeMemberDeclaration, CFieldReference>() {
                    @Override
                    public CFieldReference apply(CCompositeTypeMemberDeclaration field) {
                        return new CFieldReference(loc, field.getType(), field.getName(), currentSubobject, false);
                    }
                }).iterator();

        if (!fields.hasNext()) {
            // empty struct
            return false;
        }

        CFieldReference designatedField = null;

        if (startingFieldName.isPresent()) {
            // find the designated field and advance the iterator up to this point
            while (fields.hasNext()) {
                CFieldReference f = fields.next();
                if (f.getFieldName().equals(startingFieldName.get())) {
                    designatedField = f;
                    break;
                }
            }
            if (designatedField == null) {
                throw new UnrecognizedCCodeException("Initializer for field " + startingFieldName.get()
                        + " but no field with this name exists in " + structType, edge, designator);
            }

        } else {
            // first field
            designatedField = fields.next();
        }

        currentSubobjects.push(designatedField);

        switch (structType.getKind()) {
        case STRUCT:
            nextSubobjects.push(CInitializers.<CExpression>safeCast(fields));
            break;
        case UNION:
            // unions only have their first field initialized, ignore the rest
            nextSubobjects.push(Collections.<CExpression>emptyIterator());
            break;
        default:
            throw new AssertionError();
        }

        return true;
    }

    /**
     * Handle the case when the current subobject that will be initialized next
     * is an array.
     * This method only prepares the two stacks by pushing one object on both
     * of them (for the next element to be initialized, and the iterator for the
     * remainder of the elements).
     * @param currentSubobject The struct/union to be initialized
     * @param startIndex The index of the first element to be initialized
     * @param arrayType The type of currentSubobject
     * @param currentSubobjects as in {@link #handleInitializerList(CExpression, CInitializerList, FileLocation, CFAEdge)}
     * @param nextSubobjects as in {@link #handleInitializerList(CExpression, CInitializerList, FileLocation, CFAEdge)}
     */
    private static boolean handleInitializerForArray(final CExpression currentSubobject, final long startIndex,
            final CArrayType arrayType, final Deque<CExpression> currentSubobjects,
            final Deque<Iterator<CExpression>> nextSubobjects, final FileLocation loc, final CFAEdge edge,
            final CDesignator designator) throws UnrecognizedCCodeException {

        Range<Long> arrayIndices;
        if (arrayType.getLength() instanceof CIntegerLiteralExpression) {
            // fixed-size array
            BigInteger size = ((CIntegerLiteralExpression) arrayType.getLength()).getValue();
            if (!BigInteger.valueOf(size.longValue()).equals(size)) {
                throw new UnrecognizedCCodeException(
                        "Size of type " + arrayType + " is too large to initialize explicitly", edge, designator);
            }
            // TODO use DiscreteDomain.bigintegers() when it's available.

            arrayIndices = Range.closedOpen(startIndex, size.longValue());

        } else if (arrayType.getLength() == null) {
            // variable-length array, this array goes until there are no more initializer values

            arrayIndices = Range.atLeast(startIndex);

        } else {
            throw new UnrecognizedCCodeException(
                    "Cannot initialize arrays with variable modified type like " + arrayType, edge, designator);
        }

        if (arrayIndices.isEmpty()) {
            return false;
        }

        final CType elementType = arrayType.getType();

        Set<Long> indexSet = ContiguousSet.create(arrayIndices, DiscreteDomain.longs());
        Iterator<CExpression> elements = from(indexSet).transform(new Function<Long, CExpression>() {
            @Override
            public CExpression apply(Long pInput) {
                CExpression index = new CIntegerLiteralExpression(loc, CNumericTypes.INT,
                        BigInteger.valueOf(pInput.longValue()));

                return new CArraySubscriptExpression(loc, elementType, currentSubobject, index);
            }
        }).iterator();

        CExpression firstElement = elements.next();

        currentSubobjects.push(firstElement);
        nextSubobjects.push(elements);

        return true;
    }
}