All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.fraunhofer.aisec.cpg.TranslationManager Maven / Gradle / Ivy

Go to download

A simple library to extract a code property graph out of source code. It has support for multiple passes that can extend the analysis after the graph is constructed.

There is a newer version: 8.3.0
Show newest version
/*
 * Copyright (c) 2019, Fraunhofer AISEC. 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.
 *
 *                    $$$$$$\  $$$$$$$\   $$$$$$\
 *                   $$  __$$\ $$  __$$\ $$  __$$\
 *                   $$ /  \__|$$ |  $$ |$$ /  \__|
 *                   $$ |      $$$$$$$  |$$ |$$$$\
 *                   $$ |      $$  ____/ $$ |\_$$ |
 *                   $$ |  $$\ $$ |      $$ |  $$ |
 *                   \$$$$$   |$$ |      \$$$$$   |
 *                    \______/ \__|       \______/
 *
 */

package de.fraunhofer.aisec.cpg;

import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend;
import de.fraunhofer.aisec.cpg.frontends.LanguageFrontendFactory;
import de.fraunhofer.aisec.cpg.frontends.TranslationException;
import de.fraunhofer.aisec.cpg.graph.TypeManager;
import de.fraunhofer.aisec.cpg.helpers.Benchmark;
import de.fraunhofer.aisec.cpg.helpers.Util;
import de.fraunhofer.aisec.cpg.passes.Pass;
import de.fraunhofer.aisec.cpg.passes.scopes.ScopeManager;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** Main entry point for all source code translation for all language front-ends. */
public class TranslationManager {

  private static final Logger log = LoggerFactory.getLogger(TranslationManager.class);

  @NonNull private TranslationConfiguration config;
  private AtomicBoolean isCancelled = new AtomicBoolean(false);

  private TranslationManager(@NonNull TranslationConfiguration config) {
    this.config = config;
  }

  public static Builder builder() {
    return new Builder();
  }

  /**
   * Kicks off the analysis.
   *
   * 

This method orchestrates all passes that will do the main work. * * @return a {@link CompletableFuture} with the {@link TranslationResult}. */ public CompletableFuture analyze() { TranslationResult result = new TranslationResult(this); // We wrap the analysis in a CompletableFuture, i.e. in an asynch task. return CompletableFuture.supplyAsync( () -> { ScopeManager scopesBuildForAnalysis = new ScopeManager(); Benchmark outerBench = new Benchmark(TranslationManager.class, "Translation into full graph"); Set passesNeedCleanup = new HashSet<>(); Set frontendsNeedCleanup = null; try { // Parse Java/C/CPP files Benchmark bench = new Benchmark(this.getClass(), "Frontend"); frontendsNeedCleanup = runFrontends(result, this.config, scopesBuildForAnalysis); bench.stop(); // TODO: Find a way to identify the right language during the execution of a pass (and // set the lang to the scope manager) // Apply passes for (Pass pass : config.getRegisteredPasses()) { passesNeedCleanup.add(pass); bench = new Benchmark(pass.getClass(), "Executing Pass"); pass.accept(result); bench.stop(); if (result.isCancelled()) { log.warn("Analysis interrupted, stopping Pass evaluation"); } } } catch (TranslationException ex) { throw new CompletionException(ex); } finally { outerBench.stop(); log.debug("Cleaning up {} Passes", passesNeedCleanup.size()); passesNeedCleanup.forEach(Pass::cleanup); if (frontendsNeedCleanup != null) { log.debug("Cleaning up {} Frontends", frontendsNeedCleanup.size()); frontendsNeedCleanup.forEach(LanguageFrontend::cleanup); } TypeManager.getInstance().cleanup(); } return result; }); } public List getPasses() { return config.getRegisteredPasses(); } public boolean isCancelled() { return isCancelled.get(); } /** * Parses all language files using the respective {@link LanguageFrontend} and creates the initial * set of AST nodes. * * @param result the translation result that is being mutated * @param config the translation configuration * @throws TranslationException if the language front-end runs into an error and failOnError * is true. */ private HashSet runFrontends( @NonNull TranslationResult result, @NonNull TranslationConfiguration config, @NonNull ScopeManager scopeManager) throws TranslationException { List sourceLocations = new ArrayList<>(this.config.getSourceLocations()); HashSet usedFrontends = new HashSet<>(); for (int i = 0; i < sourceLocations.size(); i++) { File sourceLocation = sourceLocations.get(i); // Recursively add files in directories if (sourceLocation.isDirectory()) { try (Stream stream = Files.find(sourceLocation.toPath(), 999, (p, fileAttr) -> fileAttr.isRegularFile())) { sourceLocations.addAll(stream.map(Path::toFile).collect(Collectors.toSet())); continue; } catch (IOException e) { log.error(e.getMessage(), e); } } log.info("Parsing {}", sourceLocation.getAbsolutePath()); LanguageFrontend frontend = null; try { frontend = LanguageFrontendFactory.getFrontend( Util.getExtension(sourceLocation), config, scopeManager); if (frontend == null) { log.error("Found no parser frontend for {}", sourceLocation.getName()); if (config.failOnError) { throw new TranslationException( "Found no parser frontend for " + sourceLocation.getName()); } continue; } for (LanguageFrontend previous : usedFrontends) { if (!previous.getClass().equals(frontend.getClass())) { log.error( "Different frontends are used for multiple files. This will very likely break the following passes."); } } usedFrontends.add(frontend); // remember which frontend parsed each file Map sfToFe = (Map) result .getScratch() .computeIfAbsent( TranslationResult.SOURCE_LOCATIONS_TO_FRONTEND, x -> new HashMap()); sfToFe.put(sourceLocation.getName(), frontend.getClass().getSimpleName()); result.getTranslationUnits().add(frontend.parse(sourceLocation)); } catch (TranslationException ex) { log.error( "An error occurred during parsing of {}: {}", sourceLocation.getName(), ex.getMessage()); if (config.failOnError) { throw ex; } } // Set frontend so passes know what language they are working on. for (Pass pass : config.getRegisteredPasses()) { pass.setLang(frontend); } } return usedFrontends; } /** * Returns the current (immutable) configuration of this TranslationManager. * * @return the configuration */ @NonNull public TranslationConfiguration getConfig() { return this.config; } public static class Builder { private TranslationConfiguration config; private Builder() {} public Builder config(TranslationConfiguration config) { this.config = config; return this; } public TranslationManager build() { return new TranslationManager(this.config); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy