diff --git a/cpg-library/src/main/java/de/fraunhofer/aisec/cpg/TranslationManager.java b/cpg-library/src/main/java/de/fraunhofer/aisec/cpg/TranslationManager.java
deleted file mode 100644
index 7f5fd8fc00..0000000000
--- a/cpg-library/src/main/java/de/fraunhofer/aisec/cpg/TranslationManager.java
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
- * 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 static de.fraunhofer.aisec.cpg.frontends.cpp.CXXLanguageFrontend.CXX_EXTENSIONS;
-
-import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend;
-import de.fraunhofer.aisec.cpg.frontends.TranslationException;
-import de.fraunhofer.aisec.cpg.frontends.golang.GoLanguageFrontend;
-import de.fraunhofer.aisec.cpg.graph.TypeManager;
-import de.fraunhofer.aisec.cpg.helpers.Benchmark;
-import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker;
-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.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.*;
-import java.util.Map.Entry;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionException;
-import java.util.concurrent.ExecutionException;
-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.checkerframework.checker.nullness.qual.Nullable;
-import org.jetbrains.annotations.NotNull;
-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();
- if (!this.config.disableCleanup) {
- 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 Set runFrontends(
- @NonNull TranslationResult result,
- @NonNull TranslationConfiguration config,
- @NonNull ScopeManager scopeManager)
- throws TranslationException {
-
- List sourceLocations = new ArrayList<>(this.config.getSourceLocations());
-
- if (config.useUnityBuild) {
- try {
- File tmpFile = Files.createTempFile("compile", ".cpp").toFile();
- tmpFile.deleteOnExit();
-
- try (var writer = new PrintWriter(tmpFile)) {
- 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()));
- }
- } else {
- if (CXX_EXTENSIONS.contains(Util.getExtension(sourceLocation))) {
- if (config.getTopLevel() != null) {
- Path topLevel = config.getTopLevel().toPath();
- writer.write(
- "#include \"" + topLevel.relativize(sourceLocation.toPath()) + "\"\n");
- } else {
- writer.write("#include \"" + sourceLocation.getAbsolutePath() + "\"\n");
- }
- }
- }
- }
- }
-
- sourceLocations = List.of(tmpFile);
- } catch (IOException e) {
- throw new TranslationException(e);
- }
- }
-
- boolean useParallelFrontends = config.useParallelFrontends;
-
- 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()));
- sourceLocations.remove(sourceLocation);
- } catch (IOException e) {
- log.error(e.getMessage(), e);
- }
- } else {
- if (useParallelFrontends
- && getFrontendClass(Util.getExtension(sourceLocation)) == GoLanguageFrontend.class) {
- log.warn("Parallel frontends are not yet supported for Go");
- useParallelFrontends = false;
- }
- }
- }
-
- TypeManager.setTypeSystemActive(config.typeSystemActiveInFrontend);
-
- Set usedFrontends;
- if (useParallelFrontends) {
- usedFrontends = parseParallel(result, scopeManager, sourceLocations);
- } else {
- usedFrontends = parseSequentially(result, scopeManager, sourceLocations);
- }
-
- if (!config.typeSystemActiveInFrontend) {
- TypeManager.setTypeSystemActive(true);
- result.getTranslationUnits().forEach(tu -> SubgraphWalker.activateTypes(tu, scopeManager));
- }
-
- return usedFrontends;
- }
-
- private Set parseParallel(
- @NonNull TranslationResult result,
- @NonNull ScopeManager originalScopeManager,
- Collection sourceLocations) {
- Set usedFrontends = new HashSet<>();
-
- log.info("Parallel parsing started");
- List>> futures = new ArrayList<>();
- List parallelScopeManagers = new ArrayList<>();
- Map>, File> futureToFile = new IdentityHashMap<>();
-
- for (File sourceLocation : sourceLocations) {
- ScopeManager scopeManager = new ScopeManager();
- parallelScopeManagers.add(scopeManager);
- CompletableFuture> future =
- getParsingFuture(result, scopeManager, sourceLocation);
- futures.add(future);
- futureToFile.put(future, sourceLocation);
- }
-
- for (CompletableFuture> future : futures) {
- try {
- future
- .get()
- .ifPresent(f -> handleCompletion(result, usedFrontends, futureToFile.get(future), f));
- } catch (InterruptedException | ExecutionException e) {
- log.error("Error parsing " + futureToFile.get(future), e);
- Thread.currentThread().interrupt();
- }
- }
- originalScopeManager.mergeFrom(parallelScopeManagers);
- usedFrontends.forEach(f -> f.setScopeManager(originalScopeManager));
-
- log.info("Parallel parsing completed");
-
- return usedFrontends;
- }
-
- private CompletableFuture> getParsingFuture(
- @NonNull TranslationResult result, @NonNull ScopeManager scopeManager, File sourceLocation) {
- return CompletableFuture.supplyAsync(
- () -> {
- try {
- return parse(result, scopeManager, sourceLocation);
- } catch (TranslationException e) {
- throw new RuntimeException("Error parsing " + sourceLocation, e);
- }
- });
- }
-
- private Set parseSequentially(
- @NonNull TranslationResult result,
- @NonNull ScopeManager scopeManager,
- Collection sourceLocations)
- throws TranslationException {
- Set usedFrontends = new HashSet<>();
-
- for (File sourceLocation : sourceLocations) {
-
- log.info("Parsing {}", sourceLocation.getAbsolutePath());
- parse(result, scopeManager, sourceLocation)
- .ifPresent(f -> handleCompletion(result, usedFrontends, sourceLocation, f));
- }
-
- return usedFrontends;
- }
-
- private void handleCompletion(
- @NonNull TranslationResult result,
- Set usedFrontends,
- File sourceLocation,
- LanguageFrontend f) {
- usedFrontends.add(f);
- if (usedFrontends.stream().map(Object::getClass).distinct().count() > 1) {
- log.error(
- "Different frontends are used for multiple files. This will very likely break the following passes.");
- }
-
- // remember which frontend parsed each file
- Map sfToFe =
- (Map)
- result
- .getScratch()
- .computeIfAbsent(
- TranslationResult.SOURCE_LOCATIONS_TO_FRONTEND,
- x -> new HashMap());
- sfToFe.put(sourceLocation.getName(), f.getClass().getSimpleName());
-
- // Set frontend so passes know what language they are working on.
- for (Pass pass : config.getRegisteredPasses()) {
- pass.setLang(f);
- }
- }
-
- private Optional parse(
- @NotNull TranslationResult result, @NotNull ScopeManager scopeManager, File sourceLocation)
- throws TranslationException {
- LanguageFrontend frontend = null;
- try {
- frontend = getFrontend(Util.getExtension(sourceLocation), 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());
- }
- return Optional.empty();
- }
-
- result.addTranslationUnit(frontend.parse(sourceLocation));
- } catch (TranslationException ex) {
- log.error(
- "An error occurred during parsing of {}: {}", sourceLocation.getName(), ex.getMessage());
-
- if (config.failOnError) {
- throw ex;
- }
- }
-
- return Optional.ofNullable(frontend);
- }
-
- @Nullable
- private LanguageFrontend getFrontend(String extension, ScopeManager scopeManager) {
- Class extends LanguageFrontend> clazz = getFrontendClass(extension);
-
- if (clazz != null) {
- try {
- return clazz
- .getConstructor(TranslationConfiguration.class, ScopeManager.class)
- .newInstance(this.config, scopeManager);
- } catch (InstantiationException
- | IllegalAccessException
- | InvocationTargetException
- | NoSuchMethodException e) {
- log.error("Could not instantiate language frontend {}", clazz.getName(), e);
- return null;
- }
- }
-
- return null;
- }
-
- @Nullable
- private Class extends LanguageFrontend> getFrontendClass(String extension) {
- return this.config.getFrontends().entrySet().stream()
- .filter(entry -> entry.getValue().contains(extension))
- .map(Entry::getKey)
- .findAny()
- .orElse(null);
- }
-
- /**
- * 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);
- }
- }
-}
diff --git a/cpg-library/src/main/java/de/fraunhofer/aisec/cpg/TranslationManager.kt b/cpg-library/src/main/java/de/fraunhofer/aisec/cpg/TranslationManager.kt
new file mode 100644
index 0000000000..ece6e7b078
--- /dev/null
+++ b/cpg-library/src/main/java/de/fraunhofer/aisec/cpg/TranslationManager.kt
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 2021, 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.TranslationException
+import de.fraunhofer.aisec.cpg.frontends.cpp.CXXLanguageFrontend
+import de.fraunhofer.aisec.cpg.frontends.golang.GoLanguageFrontend
+import de.fraunhofer.aisec.cpg.graph.TypeManager
+import de.fraunhofer.aisec.cpg.helpers.Benchmark
+import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker
+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.PrintWriter
+import java.lang.reflect.InvocationTargetException
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.attribute.BasicFileAttributes
+import java.util.*
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.CompletionException
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.atomic.AtomicBoolean
+import java.util.stream.Collectors
+import org.slf4j.LoggerFactory
+
+/** Main entry point for all source code translation for all language front-ends. */
+@OptIn(ExperimentalGolang::class)
+class TranslationManager
+private constructor(
+ /**
+ * Returns the current (immutable) configuration of this TranslationManager.
+ *
+ * @return the configuration
+ */
+ val config: TranslationConfiguration
+) {
+ private val isCancelled = AtomicBoolean(false)
+
+ /**
+ * Kicks off the analysis.
+ *
+ * This method orchestrates all passes that will do the main work.
+ *
+ * @return a [CompletableFuture] with the [TranslationResult].
+ */
+ fun analyze(): CompletableFuture {
+ val result = TranslationResult(this)
+
+ // We wrap the analysis in a CompletableFuture, i.e. in an async task.
+ return CompletableFuture.supplyAsync {
+ val scopesBuildForAnalysis = ScopeManager()
+ val outerBench =
+ Benchmark(TranslationManager::class.java, "Translation into full graph")
+ val passesNeedCleanup = mutableSetOf()
+ var frontendsNeedCleanup: Set? = null
+
+ try {
+ // Parse Java/C/CPP files
+ var bench = Benchmark(this.javaClass, "Frontend")
+ frontendsNeedCleanup = runFrontends(result, 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 in config.registeredPasses) {
+ passesNeedCleanup.add(pass)
+ bench = Benchmark(pass.javaClass, "Executing Pass")
+ pass.accept(result)
+ bench.stop()
+ if (result.isCancelled) {
+ log.warn("Analysis interrupted, stopping Pass evaluation")
+ }
+ }
+ } catch (ex: TranslationException) {
+ throw CompletionException(ex)
+ } finally {
+ outerBench.stop()
+ if (!config.disableCleanup) {
+ log.debug("Cleaning up {} Passes", passesNeedCleanup.size)
+
+ passesNeedCleanup.forEach { it.cleanup() }
+
+ log.debug("Cleaning up {} Frontends", frontendsNeedCleanup?.size)
+
+ frontendsNeedCleanup?.forEach { it.cleanup() }
+ TypeManager.getInstance().cleanup()
+ }
+ }
+ result
+ }
+ }
+
+ val passes: List
+ get() = config.registeredPasses
+
+ fun isCancelled(): Boolean {
+ return isCancelled.get()
+ }
+
+ /**
+ * Parses all language files using the respective [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 [TranslationConfiguration.failOnError]
+ * * is `true`.
+ */
+ @Throws(TranslationException::class)
+ private fun runFrontends(
+ result: TranslationResult,
+ config: TranslationConfiguration,
+ scopeManager: ScopeManager
+ ): Set {
+ var sourceLocations: List = ArrayList(this.config.sourceLocations)
+
+ var useParallelFrontends = config.useParallelFrontends
+
+ val list =
+ sourceLocations.flatMap { file ->
+ if (file.isDirectory) {
+ Files.find(
+ file.toPath(),
+ 999,
+ { _: Path?, fileAttr: BasicFileAttributes -> fileAttr.isRegularFile }
+ )
+ .map { it.toFile() }
+ .collect(Collectors.toList())
+ } else {
+ if (useParallelFrontends &&
+ Util.getExtension(file).frontendClass == GoLanguageFrontend::class.java
+ ) {
+ log.warn("Parallel frontends are not yet supported for Go")
+ useParallelFrontends = false
+ }
+ listOf(file)
+ }
+ }
+
+ if (config.useUnityBuild) {
+ val tmpFile = Files.createTempFile("compile", ".cpp").toFile()
+ tmpFile.deleteOnExit()
+
+ PrintWriter(tmpFile).use { writer ->
+ list.forEach {
+ if (CXXLanguageFrontend.CXX_EXTENSIONS.contains(Util.getExtension(it))) {
+ if (config.topLevel != null) {
+ val topLevel = config.topLevel.toPath()
+ writer.write(
+ """
+#include "${topLevel.relativize(it.toPath())}"
+
+""".trimIndent()
+ )
+ } else {
+ writer.write("""
+#include "${it.absolutePath}"
+
+""".trimIndent())
+ }
+ }
+ }
+ }
+
+ sourceLocations = listOf(tmpFile)
+ } else {
+ sourceLocations = list
+ }
+
+ TypeManager.setTypeSystemActive(config.typeSystemActiveInFrontend)
+
+ val usedFrontends =
+ if (useParallelFrontends) {
+ parseParallel(result, scopeManager, sourceLocations)
+ } else {
+ parseSequentially(result, scopeManager, sourceLocations)
+ }
+
+ if (!config.typeSystemActiveInFrontend) {
+ TypeManager.setTypeSystemActive(true)
+
+ result.translationUnits.forEach { SubgraphWalker.activateTypes(it, scopeManager) }
+ }
+
+ return usedFrontends
+ }
+
+ private fun parseParallel(
+ result: TranslationResult,
+ originalScopeManager: ScopeManager,
+ sourceLocations: Collection
+ ): Set {
+ val usedFrontends = mutableSetOf()
+
+ log.info("Parallel parsing started")
+
+ val futures = mutableListOf>>()
+ val parallelScopeManagers = mutableListOf()
+
+ val futureToFile: MutableMap>, File> =
+ IdentityHashMap()
+
+ for (sourceLocation in sourceLocations) {
+ val scopeManager = ScopeManager()
+ parallelScopeManagers.add(scopeManager)
+
+ val future =
+ CompletableFuture.supplyAsync {
+ try {
+ return@supplyAsync parse(result, scopeManager, sourceLocation)
+ } catch (e: TranslationException) {
+ throw RuntimeException("Error parsing $sourceLocation", e)
+ }
+ }
+
+ futures.add(future)
+ futureToFile[future] = sourceLocation
+ }
+
+ for (future in futures) {
+ try {
+ future.get().ifPresent { f: LanguageFrontend ->
+ handleCompletion(result, usedFrontends, futureToFile[future], f)
+ }
+ } catch (e: InterruptedException) {
+ log.error("Error parsing " + futureToFile[future], e)
+ Thread.currentThread().interrupt()
+ } catch (e: ExecutionException) {
+ log.error("Error parsing " + futureToFile[future], e)
+ Thread.currentThread().interrupt()
+ }
+ }
+
+ originalScopeManager.mergeFrom(parallelScopeManagers)
+ usedFrontends.forEach { it.scopeManager = originalScopeManager }
+
+ log.info("Parallel parsing completed")
+
+ return usedFrontends
+ }
+
+ @Throws(TranslationException::class)
+ private fun parseSequentially(
+ result: TranslationResult,
+ scopeManager: ScopeManager,
+ sourceLocations: Collection
+ ): Set {
+ val usedFrontends = mutableSetOf()
+
+ for (sourceLocation in sourceLocations) {
+ log.info("Parsing {}", sourceLocation.absolutePath)
+
+ parse(result, scopeManager, sourceLocation).ifPresent { f: LanguageFrontend ->
+ handleCompletion(result, usedFrontends, sourceLocation, f)
+ }
+ }
+
+ return usedFrontends
+ }
+
+ private fun handleCompletion(
+ result: TranslationResult,
+ usedFrontends: MutableSet,
+ sourceLocation: File?,
+ f: LanguageFrontend
+ ) {
+ usedFrontends.add(f)
+
+ if (usedFrontends.map { it.javaClass }.distinct().count() > 1) {
+ log.error(
+ "Different frontends are used for multiple files. This will very likely break the following passes."
+ )
+ }
+
+ // remember which frontend parsed each file
+ val sfToFe =
+ result.scratch.computeIfAbsent(TranslationResult.SOURCE_LOCATIONS_TO_FRONTEND) {
+ mutableMapOf()
+ } as
+ MutableMap
+ sfToFe[sourceLocation!!.name] = f.javaClass.simpleName
+
+ // Set frontend so passes know what language they are working on.
+ for (pass in config.registeredPasses) {
+ pass.lang = f
+ }
+ }
+
+ @Throws(TranslationException::class)
+ private fun parse(
+ result: TranslationResult,
+ scopeManager: ScopeManager,
+ sourceLocation: File
+ ): Optional {
+ var frontend: LanguageFrontend? = null
+ try {
+ frontend = getFrontend(Util.getExtension(sourceLocation), scopeManager)
+
+ if (frontend == null) {
+ log.error("Found no parser frontend for {}", sourceLocation.name)
+
+ if (config.failOnError) {
+ throw TranslationException(
+ "Found no parser frontend for " + sourceLocation.name
+ )
+ }
+ return Optional.empty()
+ }
+ result.addTranslationUnit(frontend.parse(sourceLocation))
+ } catch (ex: TranslationException) {
+ log.error("An error occurred during parsing of {}: {}", sourceLocation.name, ex.message)
+ if (config.failOnError) {
+ throw ex
+ }
+ }
+ return Optional.ofNullable(frontend)
+ }
+
+ private fun getFrontend(extension: String, scopeManager: ScopeManager): LanguageFrontend? {
+ val clazz = extension.frontendClass
+
+ return if (clazz != null) {
+ try {
+ clazz
+ .getConstructor(TranslationConfiguration::class.java, ScopeManager::class.java)
+ .newInstance(config, scopeManager)
+ } catch (e: InstantiationException) {
+ log.error("Could not instantiate language frontend {}", clazz.name, e)
+ null
+ } catch (e: IllegalAccessException) {
+ log.error("Could not instantiate language frontend {}", clazz.name, e)
+ null
+ } catch (e: InvocationTargetException) {
+ log.error("Could not instantiate language frontend {}", clazz.name, e)
+ null
+ } catch (e: NoSuchMethodException) {
+ log.error("Could not instantiate language frontend {}", clazz.name, e)
+ null
+ }
+ } else null
+ }
+
+ private val String.frontendClass: Class?
+ get() {
+ return config
+ .frontends
+ .entries
+ .filter { it.value.contains(this) }
+ .map { it.key }
+ .firstOrNull()
+ }
+
+ class Builder {
+ private var config: TranslationConfiguration = TranslationConfiguration.builder().build()
+
+ fun config(config: TranslationConfiguration): Builder {
+ this.config = config
+ return this
+ }
+
+ fun build(): TranslationManager {
+ return TranslationManager(config)
+ }
+ }
+
+ companion object {
+ private val log = LoggerFactory.getLogger(TranslationManager::class.java)
+
+ @JvmStatic
+ fun builder(): Builder {
+ return Builder()
+ }
+ }
+}
diff --git a/cpg-library/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java b/cpg-library/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java
index fd1635a6e2..00b08fe935 100644
--- a/cpg-library/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java
+++ b/cpg-library/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java
@@ -101,10 +101,10 @@ public enum Language {
private Set secondOrderTypes = Collections.synchronizedSet(new HashSet<>());
/**
- * The language frontend that is currently active. This can be null, e.g. if we are executed in tests.
+ * The language frontend that is currently active. This can be null, e.g. if we are executed in
+ * tests.
*/
- @org.jetbrains.annotations.Nullable
- private LanguageFrontend frontend;
+ @org.jetbrains.annotations.Nullable private LanguageFrontend frontend;
private boolean noFrontendWarningIssued = false;
@@ -521,7 +521,9 @@ public Language getLanguage() {
return Language.TYPESCRIPT;
}
- log.error("Unknown language (frontend: {})", frontend != null ? frontend.getClass().getSimpleName() : null);
+ log.error(
+ "Unknown language (frontend: {})",
+ frontend != null ? frontend.getClass().getSimpleName() : null);
return Language.UNKNOWN;
}