Java tutorial
/* * 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 edu.buaa.satla.analysis.cfa; import static com.google.common.base.Predicates.*; import static com.google.common.collect.FluentIterable.from; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.sosy_lab.common.Pair; import org.sosy_lab.common.log.LogManager; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import edu.buaa.satla.analysis.cfa.ast.AFunctionDeclaration; import edu.buaa.satla.analysis.cfa.ast.c.CComplexTypeDeclaration; import edu.buaa.satla.analysis.cfa.ast.c.CDeclaration; import edu.buaa.satla.analysis.cfa.ast.c.CFunctionDeclaration; import edu.buaa.satla.analysis.cfa.ast.c.CSimpleDeclaration; import edu.buaa.satla.analysis.cfa.ast.c.CTypeDeclaration; import edu.buaa.satla.analysis.cfa.ast.c.CTypeDefDeclaration; import edu.buaa.satla.analysis.cfa.ast.c.CVariableDeclaration; import edu.buaa.satla.analysis.cfa.model.CFAEdge; import edu.buaa.satla.analysis.cfa.model.CFAEdgeType; import edu.buaa.satla.analysis.cfa.model.CFANode; import edu.buaa.satla.analysis.cfa.model.FunctionCallEdge; import edu.buaa.satla.analysis.cfa.model.MultiEdge; import edu.buaa.satla.analysis.cfa.model.c.CDeclarationEdge; import edu.buaa.satla.analysis.cfa.parser.Scope; import edu.buaa.satla.analysis.cfa.types.c.CArrayType; import edu.buaa.satla.analysis.cfa.types.c.CComplexType; import edu.buaa.satla.analysis.cfa.types.c.CCompositeType; import edu.buaa.satla.analysis.cfa.types.c.CElaboratedType; import edu.buaa.satla.analysis.cfa.types.c.CEnumType; import edu.buaa.satla.analysis.cfa.types.c.CFunctionType; import edu.buaa.satla.analysis.cfa.types.c.CPointerType; import edu.buaa.satla.analysis.cfa.types.c.CProblemType; import edu.buaa.satla.analysis.cfa.types.c.CSimpleType; import edu.buaa.satla.analysis.cfa.types.c.CType; import edu.buaa.satla.analysis.cfa.types.c.CTypeVisitor; import edu.buaa.satla.analysis.cfa.types.c.CTypedefType; import edu.buaa.satla.analysis.cfa.types.c.CComplexType.ComplexTypeKind; import edu.buaa.satla.analysis.cfa.types.c.CCompositeType.CCompositeTypeMemberDeclaration; import edu.buaa.satla.analysis.util.CFAUtils; /** * Used to store the types of the cfa that are * lost when only a single or a block of statements * of the original program is parsed. */ public class CProgramScope implements Scope { private static final Function<CFANode, Iterable<? extends CSimpleDeclaration>> TO_C_SIMPLE_DECLARATIONS = new Function<CFANode, Iterable<? extends CSimpleDeclaration>>() { @Override public Iterable<? extends CSimpleDeclaration> apply(CFANode pNode) { return CFAUtils.leavingEdges(pNode) .transformAndConcat(new Function<CFAEdge, Iterable<? extends CSimpleDeclaration>>() { @Override public Iterable<? extends CSimpleDeclaration> apply(CFAEdge pEdge) { if (pEdge.getEdgeType() == CFAEdgeType.DeclarationEdge) { CDeclaration dcl = ((CDeclarationEdge) pEdge).getDeclaration(); return Collections.singleton(dcl); } if (pEdge.getEdgeType() == CFAEdgeType.FunctionCallEdge) { FunctionCallEdge fce = (FunctionCallEdge) pEdge; AFunctionDeclaration decl = fce.getSuccessor().getFunctionDefinition(); return from(decl.getParameters()).filter(CSimpleDeclaration.class); } if (pEdge.getEdgeType() == CFAEdgeType.MultiEdge) { MultiEdge edge = (MultiEdge) pEdge; Collection<CSimpleDeclaration> result = new ArrayList<>(); for (CFAEdge innerEdge : edge.getEdges()) { Iterables.addAll(result, apply(innerEdge)); } return result; } return Collections.emptySet(); } }); } }; private static final Predicate<CSimpleDeclaration> HAS_NAME = new Predicate<CSimpleDeclaration>() { @Override public boolean apply(CSimpleDeclaration pDeclaration) { return pDeclaration.getName() != null && pDeclaration.getQualifiedName() != null; } }; private static final Function<CSimpleDeclaration, String> GET_NAME = new Function<CSimpleDeclaration, String>() { @Override public String apply(CSimpleDeclaration pDeclaration) { return pDeclaration.getName(); } }; private static final Function<CSimpleDeclaration, String> GET_ORIGINAL_QUALIFIED_NAME = new Function<CSimpleDeclaration, String>() { @Override public String apply(CSimpleDeclaration pDeclaration) { String name = pDeclaration.getName(); String originalName = pDeclaration.getOrigName(); String qualifiedName = pDeclaration.getQualifiedName(); if (name.equals(originalName)) { return qualifiedName; } assert qualifiedName.endsWith(name); return qualifiedName.substring(0, qualifiedName.length() - name.length()) + originalName; } }; private final String currentFile = ""; private final Set<String> variableNames; private final Map<String, CSimpleDeclaration> uniqueSimpleDeclarations; private final Multimap<String, CFunctionDeclaration> functionDeclarations; private final Map<String, CSimpleDeclaration> qualifiedDeclarations; private final Map<String, CType> qualifiedTypeDefs; // TODO map type declarations to types and construct types that have original names for witness automaton parsing private final Map<String, CComplexType> qualifiedTypes; private final String functionName; /** * Returns an empty program scope. */ private CProgramScope() { variableNames = Collections.emptySet(); qualifiedDeclarations = Collections.emptyMap(); uniqueSimpleDeclarations = Collections.emptyMap(); functionDeclarations = ImmutableListMultimap.of(); qualifiedTypes = Collections.emptyMap(); qualifiedTypeDefs = Collections.emptyMap(); functionName = null; } /** * Copies the given program scope but with the given function name. * * @param pScope the old scope. * @param pFunctionName the new function name. */ private CProgramScope(CProgramScope pScope, String pFunctionName) { variableNames = pScope.variableNames; uniqueSimpleDeclarations = pScope.uniqueSimpleDeclarations; functionDeclarations = pScope.functionDeclarations; qualifiedDeclarations = pScope.qualifiedDeclarations; qualifiedTypes = pScope.qualifiedTypes; qualifiedTypeDefs = pScope.qualifiedTypeDefs; functionName = pFunctionName; } /** * Creates an object of this class. * * When a single or a block of statements is supposed to be parsed, first a cfa for * the whole program has to be parsed to generate complex types for the variables. * These types and declarations are stored in this scope. * * @param cfa the cfa of the program, where single or block of statements are supposed to be parsed */ public CProgramScope(CFA cfa, LogManager pLogger) { assert cfa.getLanguage() == Language.C; functionName = null; /* Get all nodes, get all edges from nodes, get all declarations from edges, * assign every declaration its name. */ Collection<CFANode> nodes = cfa.getAllNodes(); FluentIterable<CSimpleDeclaration> dcls = FluentIterable.from(nodes) .transformAndConcat(TO_C_SIMPLE_DECLARATIONS).filter(HAS_NAME); FluentIterable<CFunctionDeclaration> functionDcls = dcls.filter(CFunctionDeclaration.class); FluentIterable<CSimpleDeclaration> nonFunctionDcls = dcls .filter(not(instanceOf(CFunctionDeclaration.class))); FluentIterable<CTypeDeclaration> typeDcls = dcls.filter(CTypeDeclaration.class); qualifiedTypes = extractTypes(nonFunctionDcls, pLogger); qualifiedTypeDefs = extractTypeDefs(typeDcls, pLogger); functionDeclarations = functionDcls.index(GET_ORIGINAL_QUALIFIED_NAME); variableNames = nonFunctionDcls.transform(GET_NAME).toSet(); qualifiedDeclarations = extractQualifiedDeclarations(nonFunctionDcls, pLogger); uniqueSimpleDeclarations = extractUniqueSimpleDeclarations(qualifiedDeclarations); } public static CProgramScope empty() { return new CProgramScope(); } @Override public boolean isGlobalScope() { return false; } @Override public boolean variableNameInUse(String pName, String pOrigName) { return variableNames.contains(pName); } @Override public CSimpleDeclaration lookupVariable(String pName) { CSimpleDeclaration result; if (simulatesFunctionScope()) { result = qualifiedDeclarations.get(getCurrentFunctionName() + "::" + pName); if (result != null) { return result; } } result = qualifiedDeclarations.get(pName); if (result != null) { return result; } return uniqueSimpleDeclarations.get(pName); } @Override public CFunctionDeclaration lookupFunction(String pName) { // Just take the first declaration; multiple different ones are not allowed for (CFunctionDeclaration result : functionDeclarations.get(pName)) { return result; } return null; } @Override public CComplexType lookupType(String pName) { CComplexType result = null; if (simulatesFunctionScope()) { String functionQualifiedName = getCurrentFunctionName() + "::" + pName; result = lookupQualifiedComplexType(functionQualifiedName, qualifiedTypes); if (result != null) { return result; } result = qualifiedTypes.get(functionQualifiedName); if (result != null) { return result; } } result = lookupQualifiedComplexType(pName, qualifiedTypes); if (result != null) { return result; } result = qualifiedTypes.get(pName); if (result != null) { return result; } CType typdefResult = lookupTypedef(pName); if (typdefResult instanceof CComplexType) { return (CComplexType) typdefResult; } return null; } @Override public CType lookupTypedef(String pName) { CType result = null; if (simulatesFunctionScope()) { String functionQualifiedName = getCurrentFunctionName() + "::" + pName; result = lookupQualifiedComplexType(functionQualifiedName, qualifiedTypeDefs); if (result != null) { return result; } result = qualifiedTypeDefs.get(functionQualifiedName); if (result != null) { return result; } } result = lookupQualifiedComplexType(pName, qualifiedTypeDefs); if (result != null) { return result; } return qualifiedTypeDefs.get(pName); } @Override public void registerDeclaration(CSimpleDeclaration pDeclaration) { // Assume that all declarations are already registered } @Override public boolean registerTypeDeclaration(CComplexTypeDeclaration pDeclaration) { // Assume that all type declarations are already registered return false; } @Override public String createScopedNameOf(String pName) { if (simulatesFunctionScope()) { return getCurrentFunctionName() + "::" + pName; } return pName; } /** * Returns the name for the type as it would be if it is renamed. */ @Override public String getRenamedTypeName(String type) { return type + "__" + currentFile; } /** * Create a CProgramScope that tries to simulate a function scope. * * @param pFunctionName the * @return */ public CProgramScope createFunctionScope(String pFunctionName) { return new CProgramScope(this, pFunctionName); } public boolean simulatesFunctionScope() { return functionName != null; } public String getCurrentFunctionName() { Preconditions.checkState(simulatesFunctionScope()); return functionName; } private static boolean equals(CType pA, CType pB) { return equals(pA, pB, Sets.<Pair<CType, CType>>newHashSet()); } private static boolean equals(@Nullable CType pA, @Nullable CType pB, Set<Pair<CType, CType>> pResolved) { // Identity check if (pA == pB) { return true; } // Exactly one of both null if (pA == null) { return false; } Pair<CType, CType> ab = Pair.of(pA, pB); // Check if equality is already known if (pResolved.contains(ab)) { return true; } // Standard equals check (non-recursive for composite types) boolean nonRecEq = pA.equals(pB); if (!nonRecEq) { return false; } // If the types are not composite types, we are done if (!(pA instanceof CCompositeType)) { pResolved.add(ab); return true; } CCompositeType aComp = (CCompositeType) pA; CCompositeType bComp = (CCompositeType) pB; // Check member count if (aComp.getMembers().size() != bComp.getMembers().size()) { return false; } // Assume they are equal for the recursive check pResolved.add(ab); Iterator<CCompositeTypeMemberDeclaration> aMembers = aComp.getMembers().iterator(); for (CCompositeTypeMemberDeclaration bMember : bComp.getMembers()) { if (!equals(aMembers.next().getType(), bMember.getType(), pResolved)) { pResolved.remove(ab); return false; } } return true; } private static Map<String, CSimpleDeclaration> extractQualifiedDeclarations( FluentIterable<CSimpleDeclaration> pNonFunctionDcls, LogManager pLogger) { Multimap<String, CSimpleDeclaration> qualifiedDeclarationsMultiMap = pNonFunctionDcls .index(GET_ORIGINAL_QUALIFIED_NAME); Map<String, CSimpleDeclaration> qualifiedDeclarations = Maps.newHashMap(); for (Map.Entry<String, Collection<CSimpleDeclaration>> declarationsEntry : qualifiedDeclarationsMultiMap .asMap().entrySet()) { String qualifiedName = declarationsEntry.getKey(); Collection<CSimpleDeclaration> declarations = declarationsEntry.getValue(); Set<CSimpleDeclaration> duplicateFreeDeclarations = from(declarations) .transform(new Function<CSimpleDeclaration, CSimpleDeclaration>() { @Override public CSimpleDeclaration apply(CSimpleDeclaration pArg0) { if (pArg0 instanceof CVariableDeclaration) { CVariableDeclaration original = (CVariableDeclaration) pArg0; if (original.getInitializer() == null) { return pArg0; } return new CVariableDeclaration(original.getFileLocation(), original.isGlobal(), original.getCStorageClass(), original.getType(), original.getName(), original.getOrigName(), original.getQualifiedName(), null); } return pArg0; } }).toSet(); if (!duplicateFreeDeclarations.isEmpty()) { if (duplicateFreeDeclarations.size() == 1) { qualifiedDeclarations.put(qualifiedName, declarations.iterator().next()); } else { pLogger.log(Level.FINEST, "Ignoring declaration for", qualifiedName, " for creation of program-wide scope because it is not unique."); } } } return Collections.unmodifiableMap(qualifiedDeclarations); } private static Map<String, CComplexType> extractTypes(FluentIterable<? extends CSimpleDeclaration> pDcls, LogManager pLogger) { // Collect all types TypeCollector typeCollector = new TypeCollector(); for (CSimpleDeclaration declaration : pDcls) { declaration.getType().accept(typeCollector); } // Construct multimap that may contain duplicates Multimap<String, CComplexType> typesMap = from(typeCollector.getCollectedTypes()).filter(CComplexType.class) .index(new Function<CComplexType, String>() { @Override public String apply(CComplexType pArg0) { return pArg0.getQualifiedName(); } }); // Get unique types Map<String, CComplexType> uniqueTypes = Maps.newHashMap(); for (Map.Entry<String, Collection<CComplexType>> typeEntry : typesMap.asMap().entrySet()) { String qualifiedName = typeEntry.getKey(); Collection<CComplexType> types = typeEntry.getValue(); putIfUnique(uniqueTypes, qualifiedName, types, pLogger); } return Collections.unmodifiableMap(uniqueTypes); } private static Map<String, CType> extractTypeDefs(FluentIterable<CTypeDeclaration> pTypeDcls, LogManager pLogger) { FluentIterable<CTypeDefDeclaration> plainTypeDefs = pTypeDcls.filter(CTypeDefDeclaration.class); // Construct multimap that may contain duplicates Multimap<String, CTypeDefDeclaration> typeDefDeclarationsMap = plainTypeDefs .index(new Function<CTypeDefDeclaration, String>() { @Override public String apply(CTypeDefDeclaration pArg0) { return pArg0.getQualifiedName(); } }); // Get unique type defs Map<String, CType> uniqueTypeDefs = Maps.newHashMap(); for (Map.Entry<String, Collection<CTypeDefDeclaration>> typeDefEntry : typeDefDeclarationsMap.asMap() .entrySet()) { String qualifiedName = typeDefEntry.getKey(); FluentIterable<CType> types = from(typeDefEntry.getValue()) .transform(new Function<CTypeDefDeclaration, CType>() { @Override public CType apply(CTypeDefDeclaration pArg0) { return pArg0.getType(); } }); putIfUnique(uniqueTypeDefs, qualifiedName, types, pLogger); } return Collections.unmodifiableMap(uniqueTypeDefs); } private static Map<String, CSimpleDeclaration> extractUniqueSimpleDeclarations( Map<String, CSimpleDeclaration> pQualifiedDeclarations) { return Maps .transformEntries(Maps.filterEntries(from(pQualifiedDeclarations.values()).index(GET_NAME).asMap(), new Predicate<Map.Entry<String, Collection<CSimpleDeclaration>>>() { @Override public boolean apply(Entry<String, Collection<CSimpleDeclaration>> pArg0) { return pArg0.getValue().size() == 1; } }), new Maps.EntryTransformer<String, Collection<CSimpleDeclaration>, CSimpleDeclaration>() { @Override public CSimpleDeclaration transformEntry(String pArg0, @Nonnull Collection<CSimpleDeclaration> pArg1) { return pArg1.iterator().next(); } }); } private static <T extends CType> void putIfUnique(Map<String, ? super T> pTarget, String pQualifiedName, Iterable<? extends T> pValues, LogManager pLogger) { if (!Iterables.isEmpty(pValues)) { Iterator<? extends T> typeIterator = pValues.iterator(); T firstType = typeIterator.next(); // Check that all types are the same; resolve elaborated types before checking CType firstChecktype = resolveElaboratedTypeForEqualityCheck(firstType); boolean duplicateFound = false; while (typeIterator.hasNext() && !duplicateFound) { if (!equals(firstChecktype, resolveElaboratedTypeForEqualityCheck(typeIterator.next()))) { // Does not seem to happen in competition benchmark set, so we should be fine pLogger.log(Level.FINEST, "Ignoring declaration for", pQualifiedName, " for creation of program-wide scope because it is not unique."); duplicateFound = true; } } if (!duplicateFound) { // Prefer recording elaborated types for (T type : pValues) { if (type instanceof CElaboratedType) { pTarget.put(pQualifiedName, type); return; } } pTarget.put(pQualifiedName, firstType); } } } private static CType resolveElaboratedTypeForEqualityCheck(CType pType) { CType currentType = pType; while (currentType instanceof CElaboratedType) { currentType = ((CElaboratedType) currentType).getRealType(); } return currentType; } private static <T> T lookupQualifiedComplexType(String pName, Map<String, T> pStorage) { Set<T> potentialResults = Sets.newHashSet(); for (ComplexTypeKind kind : ComplexTypeKind.values()) { T potentialResult = pStorage.get(kind.toASTString() + " " + pName); if (potentialResult != null) { potentialResults.add(potentialResult); } } if (potentialResults.size() == 1) { return potentialResults.iterator().next(); } return null; } private static class TypeCollector implements CTypeVisitor<Void, RuntimeException> { private final Set<CType> collectedTypes; public TypeCollector() { this(Sets.<CType>newHashSet()); } public TypeCollector(Set<CType> pCollectedTypes) { this.collectedTypes = pCollectedTypes; } public Set<CType> getCollectedTypes() { return Collections.unmodifiableSet(collectedTypes); } @Override public Void visit(CArrayType pArrayType) throws RuntimeException { if (!collectedTypes.contains(pArrayType)) { collectedTypes.add(pArrayType); pArrayType.getType().accept(this); if (pArrayType.getLength() != null) { pArrayType.getLength().getExpressionType().accept(this); } } return null; } @Override public Void visit(CCompositeType pCompositeType) throws RuntimeException { if (!collectedTypes.contains(pCompositeType)) { collectedTypes.add(pCompositeType); for (CCompositeTypeMemberDeclaration member : pCompositeType.getMembers()) { member.getType().accept(this); } } return null; } @Override public Void visit(CElaboratedType pElaboratedType) throws RuntimeException { if (!collectedTypes.contains(pElaboratedType)) { collectedTypes.add(pElaboratedType); if (pElaboratedType.getRealType() != null) { pElaboratedType.getRealType().accept(this); } } return null; } @Override public Void visit(CEnumType pEnumType) throws RuntimeException { if (!collectedTypes.contains(pEnumType)) { collectedTypes.add(pEnumType); } return null; } @Override public Void visit(CFunctionType pFunctionType) throws RuntimeException { if (!collectedTypes.contains(pFunctionType)) { collectedTypes.add(pFunctionType); for (CType parameterType : pFunctionType.getParameters()) { parameterType.accept(this); } } return null; } @Override public Void visit(CPointerType pPointerType) throws RuntimeException { if (!collectedTypes.contains(pPointerType)) { collectedTypes.add(pPointerType); pPointerType.getType().accept(this); } return null; } @Override public Void visit(CProblemType pProblemType) throws RuntimeException { if (!collectedTypes.contains(pProblemType)) { collectedTypes.add(pProblemType); } return null; } @Override public Void visit(CSimpleType pSimpleType) throws RuntimeException { if (!collectedTypes.contains(pSimpleType)) { collectedTypes.add(pSimpleType); } return null; } @Override public Void visit(CTypedefType pTypedefType) throws RuntimeException { if (!collectedTypes.contains(pTypedefType)) { collectedTypes.add(pTypedefType); pTypedefType.getRealType().accept(this); } return null; } } }