From 9ffe8ca950b1a172236b891ae8367288580a7e3a Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Tue, 14 Sep 2021 22:07:59 +0200 Subject: [PATCH] Converting `TranslationManager` to Kotlin (#562) --- .../aisec/cpg/TranslationManager.java | 411 ------------------ .../aisec/cpg/TranslationManager.kt | 402 +++++++++++++++++ .../aisec/cpg/graph/TypeManager.java | 10 +- 3 files changed, 408 insertions(+), 415 deletions(-) delete mode 100644 cpg-library/src/main/java/de/fraunhofer/aisec/cpg/TranslationManager.java create mode 100644 cpg-library/src/main/java/de/fraunhofer/aisec/cpg/TranslationManager.kt 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 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 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; }