Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make language frontends configurable #389

Merged
merged 13 commits into from
Apr 15, 2021
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ import de.fraunhofer.aisec.cpg.TranslationManager;
import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration;

var path = Paths.get("src/test/resources/openssl/client.cpp");
var config = TranslationConfiguration.builder().sourceLocations(path.toFile()).defaultPasses().debugParser(true).build();
var config = TranslationConfiguration.builder().sourceLocations(path.toFile()).defaultPasses().defaultLanguages().debugParser(true).build();
var analyzer = TranslationManager.builder().config(config).build();
var result = analyzer.analyze().get();
var tu = result.getTranslationUnits().get(0);
Expand Down
3 changes: 3 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ spotless {
)
googleJavaFormat()
}
kotlin {
ktfmt().kotlinlangStyle()
}
}

if (project.hasProperty("experimental")) {
Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=512m
7 changes: 2 additions & 5 deletions src/main/java/de/fraunhofer/aisec/cpg/ExperimentalGolang.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
package de.fraunhofer.aisec.cpg

/**
* This annotation marks the use an experimental Golang language feature.
*/
@RequiresOptIn
annotation class ExperimentalGolang
/** This annotation marks the use an experimental Golang language feature. */
@RequiresOptIn annotation class ExperimentalGolang
7 changes: 2 additions & 5 deletions src/main/java/de/fraunhofer/aisec/cpg/ExperimentalGraph.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
package de.fraunhofer.aisec.cpg

/**
* This annotation marks the use an experimental graph feature.
*/
@RequiresOptIn
annotation class ExperimentalGraph
/** This annotation marks the use an experimental graph feature. */
@RequiresOptIn annotation class ExperimentalGraph
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@

package de.fraunhofer.aisec.cpg;

import static de.fraunhofer.aisec.cpg.frontends.cpp.CXXLanguageFrontend.CXX_EXTENSIONS;
import static de.fraunhofer.aisec.cpg.frontends.cpp.CXXLanguageFrontend.CXX_HEADER_EXTENSIONS;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend;
import de.fraunhofer.aisec.cpg.frontends.cpp.CXXLanguageFrontend;
import de.fraunhofer.aisec.cpg.frontends.java.JavaLanguageFrontend;
import de.fraunhofer.aisec.cpg.passes.CallResolver;
import de.fraunhofer.aisec.cpg.passes.ControlFlowSensitiveDFGPass;
import de.fraunhofer.aisec.cpg.passes.EvaluationOrderGraphPass;
Expand Down Expand Up @@ -87,6 +95,12 @@ public class TranslationConfiguration {
*/
public final List<String> includeBlacklist;

/**
* This map contains a list of language frontends classes and the file types they are registered
* for.
*/
private Map<Class<? extends LanguageFrontend>, List<String>> frontends;

/**
* Switch off cleaning up TypeManager memory after analysis.
*
Expand Down Expand Up @@ -134,6 +148,7 @@ private TranslationConfiguration(
List<String> includeWhitelist,
List<String> includeBlacklist,
List<Pass> passes,
Map<Class<? extends LanguageFrontend>, List<String>> frontends,
boolean codeInNodes,
boolean processAnnotations,
boolean disableCleanup,
Expand All @@ -148,6 +163,7 @@ private TranslationConfiguration(
this.includeWhitelist = includeWhitelist;
this.includeBlacklist = includeBlacklist;
this.passes = passes != null ? passes : new ArrayList<>();
this.frontends = frontends;
// Make sure to init this AFTER sourceLocations has been set
this.codeInNodes = codeInNodes;
this.processAnnotations = processAnnotations;
Expand Down Expand Up @@ -175,6 +191,10 @@ public List<Pass> getRegisteredPasses() {
return this.passes;
}

public Map<Class<? extends LanguageFrontend>, List<String>> getFrontends() {
return this.frontends;
}

/**
* Builds a {@link TranslationConfiguration}.
*
Expand All @@ -192,6 +212,7 @@ public List<Pass> getRegisteredPasses() {
*/
public static class Builder {
private List<File> sourceLocations = new ArrayList<>();
private Map<Class<? extends LanguageFrontend>, List<String>> frontends = new HashMap<>();
private File topLevel = null;
private boolean debugParser = false;
private boolean failOnError = false;
Expand Down Expand Up @@ -322,6 +343,13 @@ public Builder registerPass(@NonNull Pass pass) {
return this;
}

/** Registers an additional {@link de.fraunhofer.aisec.cpg.frontends.LanguageFrontend}. */
public Builder registerLanguage(
@NonNull Class<? extends LanguageFrontend> frontend, List<String> fileTypes) {
this.frontends.put(frontend, fileTypes);
return this;
}

/**
* Register all default {@link Pass}es.
*
Expand Down Expand Up @@ -354,6 +382,22 @@ public Builder defaultPasses() {
return this;
}

/**
* Register all default languages.
*
* @return
*/
public Builder defaultLanguages() {
registerLanguage(
CXXLanguageFrontend.class,
Lists.newArrayList(Iterables.concat(CXX_EXTENSIONS, CXX_HEADER_EXTENSIONS)));
registerLanguage(JavaLanguageFrontend.class, JavaLanguageFrontend.JAVA_EXTENSIONS);

// do not register experimental languages by default until we have a release strategy
// registerLanguage(GoLanguageFrontend.class, GoLanguageFrontend.GOLANG_EXTENSIONS);
return this;
}

public Builder codeInNodes(boolean b) {
this.codeInNodes = b;
return this;
Expand Down Expand Up @@ -391,6 +435,7 @@ public TranslationConfiguration build() {
includeWhitelist,
includeBlacklist,
passes,
frontends,
codeInNodes,
processAnnotations,
disableCleanup,
Expand Down
35 changes: 30 additions & 5 deletions src/main/java/de/fraunhofer/aisec/cpg/TranslationManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,9 @@

package de.fraunhofer.aisec.cpg;

import static de.fraunhofer.aisec.cpg.frontends.LanguageFrontendFactory.CXX_EXTENSIONS;
import static de.fraunhofer.aisec.cpg.frontends.cpp.CXXLanguageFrontend.CXX_EXTENSIONS;

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;
Expand All @@ -39,6 +38,7 @@
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.ArrayList;
Expand All @@ -53,6 +53,7 @@
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.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -208,9 +209,7 @@ private HashSet<LanguageFrontend> runFrontends(
log.info("Parsing {}", sourceLocation.getAbsolutePath());
LanguageFrontend frontend = null;
try {
frontend =
LanguageFrontendFactory.getFrontend(
Util.getExtension(sourceLocation), config, scopeManager);
frontend = getFrontend(Util.getExtension(sourceLocation), scopeManager);
if (frontend == null) {
log.error("Found no parser frontend for {}", sourceLocation.getName());

Expand Down Expand Up @@ -258,6 +257,32 @@ private HashSet<LanguageFrontend> runFrontends(
return usedFrontends;
}

@Nullable
private LanguageFrontend getFrontend(String extension, ScopeManager scopeManager) {
var clazz =
this.config.getFrontends().entrySet().stream()
.filter(entry -> entry.getValue().contains(extension))
.map(entry -> entry.getKey())
.findAny()
.orElse(null);

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;
}

/**
* Returns the current (immutable) configuration of this TranslationManager.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
public abstract class LanguageFrontend {

protected static final Logger log = LoggerFactory.getLogger(LanguageFrontend.class);
protected final TranslationConfiguration config;
protected TranslationConfiguration config;
protected ScopeManager scopeManager;
/**
* Two data structures used to associate Objects input to a pass to results of a pass, e.g.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@
*/
public class CXXLanguageFrontend extends LanguageFrontend {

public static final List<String> CXX_EXTENSIONS = List.of(".c", ".cpp", ".cc");
public static final List<String> CXX_HEADER_EXTENSIONS = List.of(".h", ".hpp");

private static final Logger LOGGER = LoggerFactory.getLogger(CXXLanguageFrontend.class);
private final IncludeFileContentProvider includeFileContentProvider =
new InternalFileContentProvider() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@ package de.fraunhofer.aisec.cpg.frontends.golang

import de.fraunhofer.aisec.cpg.ExperimentalGolang
import de.fraunhofer.aisec.cpg.TranslationConfiguration
import de.fraunhofer.aisec.cpg.passes.scopes.ScopeManager
import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend
import kotlin.Throws
import de.fraunhofer.aisec.cpg.frontends.TranslationException
import java.io.File
import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration
import de.fraunhofer.aisec.cpg.passes.scopes.ScopeManager
import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation

class RawGolangNode {

}
import java.io.File
import kotlin.Throws

@ExperimentalGolang
class GoLanguageFrontend(config: TranslationConfiguration, scopeManager: ScopeManager?) : LanguageFrontend(config, scopeManager, ".") {
class GoLanguageFrontend(config: TranslationConfiguration, scopeManager: ScopeManager?) :
LanguageFrontend(config, scopeManager, ".") {
companion object {
@kotlin.jvm.JvmField var GOLANG_EXTENSIONS: List<String> = listOf(".go")

init {
System.loadLibrary("cpgo")
}
Expand All @@ -38,6 +37,6 @@ class GoLanguageFrontend(config: TranslationConfiguration, scopeManager: ScopeMa
}

override fun <S, T> setComment(s: S, ctx: T) {}

private external fun parseInternal(s: String?): TranslationUnitDeclaration
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@
/** Main parser for ONE Java files. */
public class JavaLanguageFrontend extends LanguageFrontend {

public static final List<String> JAVA_EXTENSIONS = List.of(".java");

public static final String THIS = "this";
public static final String ANNOTATION_MEMBER_VALUE = "value";

Expand Down
Loading