/* * Copyright (C) 2013-2015 Daniel Dietsch (dietsch@informatik.uni-freiburg.de) * Copyright (C) 2015 Markus Lindenmann (lindenmm@informatik.uni-freiburg.de) * Copyright (C) 2012-2015 Matthias Heizmann (heizmann@informatik.uni-freiburg.de) * Copyright (C) 2015 Oleksii Saukh (saukho@informatik.uni-freiburg.de) * Copyright (C) 2012-2015 Stefan Wissert * Copyright (C) 2015 University of Freiburg * * This file is part of the ULTIMATE CDTParser plug-in. * * The ULTIMATE CDTParser plug-in is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The ULTIMATE CDTParser plug-in is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the ULTIMATE CDTParser plug-in. If not, see . * * Additional permission under GNU GPL version 3 section 7: * If you modify the ULTIMATE CDTParser plug-in, or any covered work, by linking * or combining it with Eclipse RCP (or a modified version of Eclipse RCP), * containing parts covered by the terms of the Eclipse Public License, the * licensors of the ULTIMATE CDTParser plug-in grant you additional permission * to convey the resulting work. */ package de.uni_freiburg.informatik.ultimate.cdt.parser; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclaration; import org.eclipse.cdt.core.dom.ast.IASTDeclarator; import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier; import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement; import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement; import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import de.uni_freiburg.informatik.ultimate.core.model.services.ILogger; import de.uni_freiburg.informatik.ultimate.util.datastructures.relation.Pair; /** * @author Yannick Bühler * @date 12.11.2017 */ public class MultiparseSymbolTable extends ASTVisitor { /** * The logger for printing stuff */ private final ILogger mLogger; /** * A mapping (File Name, Identifier) -> Function definition (Not to a declaration, because only definitions have to * be unique!) */ private final Map, IASTFunctionDefinition> mFunctionMapping; /** * A mapping (File Name, Identifier) -> Global variable declarator */ private final Map, IASTDeclarator> mGlobalsMapping; /** * A mapping (File Name, Identifier) -> Prefixed name that is unique */ private final Map, String> mNamePrefixMapping; /** * A mapping File Name -> List which maps the files to the lists of files they include */ private final Map> mIncludeMapping; private final String mCdtPProjectHierachyFlag; private final boolean mCreateFilebasedPrefixes; /** * Constructs an empty symbol table * * @param logger * a logger * @param cdtPProjectHierachyFlag * A string representing the base path of the C project that is parsed * @param fileBasedPrefixes * true iff more than one translation unit is present and therefore variables should be prefixed */ public MultiparseSymbolTable(final ILogger logger, final String cdtPProjectHierachyFlag, final boolean fileBasedPrefixes) { mCdtPProjectHierachyFlag = cdtPProjectHierachyFlag; shouldVisitDeclarations = true; shouldVisitTranslationUnit = true; mLogger = logger; mFunctionMapping = new HashMap<>(); mGlobalsMapping = new HashMap<>(); mNamePrefixMapping = new HashMap<>(); mIncludeMapping = new HashMap<>(); mCreateFilebasedPrefixes = fileBasedPrefixes; } @Override public int visit(final IASTTranslationUnit tu) { final String fileName = normalizeCDTFilename(tu.getFilePath()); for (final IASTPreprocessorStatement stmt : tu.getAllPreprocessorStatements()) { if (stmt instanceof IASTPreprocessorIncludeStatement) { final IASTPreprocessorIncludeStatement include = (IASTPreprocessorIncludeStatement) stmt; if (include.isSystemInclude()) { if (!include.isResolved()) { mLogger.warn("System include " + include.getName() + " could not be resolved by CDT -- only built-in system includes are available."); } continue; } if (!include.isResolved()) { throw new UnsupportedOperationException("Include " + include.getName() + " could not be resolved"); } final String includedFile = normalizeCDTFilename(include.getPath()); mIncludeMapping.computeIfAbsent(fileName, x -> new ArrayList<>()).add(includedFile); } } return super.visit(tu); } @Override public int visit(final IASTDeclaration declaration) { // Ignore non-top-level declarations if (!(declaration.getParent() instanceof IASTTranslationUnit)) { return super.visit(declaration); } final String fileNameRaw = ((IASTTranslationUnit) declaration.getParent()).getFilePath(); final String fileName = normalizeCDTFilename(fileNameRaw); if (declaration instanceof IASTFunctionDefinition) { visitFunctionDefinition(fileName, (IASTFunctionDefinition) declaration); } else if (declaration instanceof IASTSimpleDeclaration) { for (final IASTDeclarator decl : ((IASTSimpleDeclaration) declaration).getDeclarators()) { if (!(decl instanceof IASTFunctionDeclarator)) { visitNonFunctionDeclarator(fileName, decl); } } if (declaration.isPartOfTranslationUnitFile()) { final IASTDeclSpecifier spec = ((IASTSimpleDeclaration) declaration).getDeclSpecifier(); if (spec instanceof IASTEnumerationSpecifier) { final Pair entry = new Pair<>(fileName, ((IASTEnumerationSpecifier) spec).getName().toString()); final String rId = generatePrefixedIdentifier(fileName, ((IASTEnumerationSpecifier) spec).getName().toString()); mNamePrefixMapping.put(entry, rId); } else if (spec instanceof IASTCompositeTypeSpecifier) { final Pair entry = new Pair<>(fileName, ((IASTCompositeTypeSpecifier) spec).getName().toString()); final String rId = generatePrefixedIdentifier(fileName, ((IASTCompositeTypeSpecifier) spec).getName().toString()); mNamePrefixMapping.put(entry, rId); } } } return super.visit(declaration); } private void visitNonFunctionDeclarator(final String inFile, final IASTDeclarator decl) { if (!decl.isPartOfTranslationUnitFile()) { // This indicates that the declaration comes from a resolved include // So this will not be put in the global variable table here return; } final Pair entry = new Pair<>(inFile, decl.getName().toString()); mGlobalsMapping.put(entry, decl); mNamePrefixMapping.put(entry, generatePrefixedIdentifier(inFile, decl.getName().toString())); } private void visitFunctionDefinition(final String inFile, final IASTFunctionDefinition fdef) { if (!fdef.isPartOfTranslationUnitFile()) { // This indicates that the definition comes from a resolved include // So this will be ignored. return; } final IASTDeclarator fdecl = fdef.getDeclarator(); final Pair entry = new Pair<>(inFile, fdecl.getName().toString()); mFunctionMapping.put(entry, fdef); // Don't rename the main method. There only may be one in the whole project. if (!fdecl.getName().toString().equals("main")) { mNamePrefixMapping.put(entry, generatePrefixedIdentifier(inFile, fdecl.getName().toString())); } } private String generatePrefixedIdentifier(final String file, final String id) { if (mCreateFilebasedPrefixes) { return id; } return "__U_MULTI_f" + file.replaceAll("[^a-zA-Z_]", "_") + "__" + id; } /** * Fetches the includes for the given file. * * @param normalizedFile * The file name, normalized. * @return The includes as normalized file names. */ public Collection getIncludesFor(final String normalizedFile) { if (!mIncludeMapping.containsKey(normalizedFile)) { return Collections.emptyList(); } return Collections.unmodifiableCollection(mIncludeMapping.get(normalizedFile)); } /** * Prints the mappings for debug purposes */ public void printMappings() { mLogger.info("Include resolver:"); for (final Map.Entry> entry : mIncludeMapping.entrySet()) { mLogger.info("File " + entry.getKey() + " includes: " + String.join(", ", entry.getValue())); } if (mIncludeMapping.isEmpty()) { mLogger.info(""); } mLogger.info("Function table:"); for (final Pair key : mFunctionMapping.keySet()) { final String newName = mNamePrefixMapping.get(key); mLogger.info("Function definition of " + newName + " ('" + key.getSecond() + "') in " + key.getFirst()); } if (mFunctionMapping.isEmpty()) { mLogger.info(""); } mLogger.info("Global variable table:"); for (final Pair key : mGlobalsMapping.keySet()) { final String newName = mNamePrefixMapping.get(key); mLogger.info("Global variable declaration of " + newName + " in " + key.getFirst()); } if (mGlobalsMapping.isEmpty()) { mLogger.info(""); } } /** * Applies a mapping of a unprefixed name to a prefixed name given the file the name was used in * * @param filePath * the file the name was used in. this is needed for include resolving / 'file-scopes' * @param name * the name that has to be mapped * @return either the mapping of the name or the name itself if there is no such mapping */ public String getNameMappingIfExists(final String filePath, final String name) { final String normalizedFile = normalizeCDTFilename(filePath); final Pair key = new Pair<>(normalizedFile, name); if (!mNamePrefixMapping.containsKey(key) && mIncludeMapping.containsKey(normalizedFile)) { // This name might be defined in an included file // So we need to resolve the includes of the current file and check all included files too final List includesOfFile = mIncludeMapping.get(normalizedFile); for (final String include : includesOfFile) { // Just try keys until one fits. This works because the n included scopes may not clash final Pair includeKey = new Pair<>(include, name); if (mNamePrefixMapping.containsKey(includeKey)) { return mNamePrefixMapping.get(includeKey); } } } // Fallback: Check the defined names in the file itself & if no definition is found, just use the original name, // i.e. no mapping of the name is performed. return mNamePrefixMapping.getOrDefault(key, name); } public String normalizeCDTFilename(final String filePath) { return CDTParser.normalizeCdtFilename(mCdtPProjectHierachyFlag, filePath); } }