-
Notifications
You must be signed in to change notification settings - Fork 360
Add Java-CPG language module #1659
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
base: develop
Are you sure you want to change the base?
Changes from all commits
f480939
6ae3fc5
0c6be35
185b311
7ab9b0f
100296a
4447224
1b900a8
722b256
4ed18ea
5536a3f
95fcb9e
640e1fa
eb592f0
88dd331
fc3d079
83995bf
9ad7388
9c25f9f
b2fc2f1
2e1e333
50b83ea
1acd991
7946823
60427fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
| <parent> | ||
| <groupId>de.jplag</groupId> | ||
| <artifactId>languages</artifactId> | ||
| <version>${revision}</version> | ||
| </parent> | ||
|
|
||
| <artifactId>java-cpg</artifactId> | ||
|
|
||
| <properties> | ||
| <cpg.version>8.0.0</cpg.version> | ||
| <cpg.groupId>de.fraunhofer.aisec</cpg.groupId> | ||
| <kotlin.version>1.9.0</kotlin.version> | ||
robinmaisch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| </properties> | ||
|
|
||
| <dependencies> | ||
|
|
||
| <dependency> | ||
| <groupId>${cpg.groupId}</groupId> | ||
| <artifactId>cpg-core</artifactId> | ||
| <version>${cpg.version}</version> | ||
|
|
||
| <exclusions> | ||
| <exclusion> | ||
| <groupId>org.apache.logging.log4j</groupId> | ||
| <artifactId>log4j-slf4j2-impl</artifactId> | ||
| </exclusion> | ||
| </exclusions> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>${cpg.groupId}</groupId> | ||
| <artifactId>cpg-language-java</artifactId> | ||
| <version>${cpg.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>${cpg.groupId}</groupId> | ||
| <artifactId>cpg-analysis</artifactId> | ||
| <version>${cpg.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.jetbrains.kotlin</groupId> | ||
| <artifactId>kotlin-stdlib-jdk8</artifactId> | ||
robinmaisch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| <version>${kotlin.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.jetbrains.kotlin</groupId> | ||
| <artifactId>kotlin-test</artifactId> | ||
| <version>${kotlin.version}</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| </dependencies> | ||
|
|
||
| <build> | ||
| <plugins> | ||
| <plugin> | ||
| <groupId>org.jetbrains.kotlin</groupId> | ||
| <artifactId>kotlin-maven-plugin</artifactId> | ||
| <version>${kotlin.version}</version> | ||
| <configuration> | ||
| <jvmTarget>1.8</jvmTarget> | ||
| <args> | ||
| <arg>-Xcontext-receivers</arg> | ||
| </args> | ||
| </configuration> | ||
| <executions> | ||
| <execution> | ||
| <id>compile</id> | ||
| <goals> | ||
| <goal>compile</goal> | ||
| </goals> | ||
| <phase>process-sources</phase> | ||
| </execution> | ||
| <execution> | ||
| <id>test-compile</id> | ||
| <goals> | ||
| <goal>test-compile</goal> | ||
| </goals> | ||
| <phase>test-compile</phase> | ||
| </execution> | ||
| </executions> | ||
| </plugin> | ||
robinmaisch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| </plugins> | ||
| </build> | ||
|
|
||
| </project> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| package de.jplag.java_cpg; | ||
|
|
||
| import java.io.File; | ||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.Set; | ||
| import java.util.concurrent.ExecutionException; | ||
|
|
||
| import de.fraunhofer.aisec.cpg.*; | ||
| import de.fraunhofer.aisec.cpg.frontends.java.JavaLanguage; | ||
| import de.fraunhofer.aisec.cpg.passes.*; | ||
| import de.jplag.ParsingException; | ||
| import de.jplag.Token; | ||
| import de.jplag.java_cpg.passes.*; | ||
| import de.jplag.java_cpg.transformation.GraphTransformation; | ||
| import de.jplag.java_cpg.transformation.GraphTransformation.ExecutionPhase; | ||
|
|
||
| import kotlin.jvm.JvmClassMappingKt; | ||
| import kotlin.reflect.KClass; | ||
|
|
||
| /** | ||
| * This class handles the transformation of files of code to a token list. | ||
| */ | ||
| public class CpgAdapter { | ||
|
|
||
| private List<Token> tokenList; | ||
| private boolean reorderingEnabled = true; | ||
|
|
||
| /** | ||
| * Constructs a new CpgAdapter. | ||
| * @param transformations a list of {@link GraphTransformation}s | ||
| */ | ||
| public CpgAdapter(GraphTransformation... transformations) { | ||
| addTransformations(transformations); | ||
| } | ||
|
|
||
| /* package-private */ List<Token> adapt(Set<File> files, boolean normalize) throws ParsingException, InterruptedException { | ||
robinmaisch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| assert !files.isEmpty(); | ||
| tokenList = null; | ||
| if (!normalize) { | ||
| clearTransformations(); | ||
| setReorderingEnabled(false); | ||
| } | ||
| // TokenizationPass sets tokenList | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. leftover comment?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The field
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, okay, this was not clear to me, thanks. |
||
|
|
||
| translate(files); | ||
|
|
||
| return tokenList; | ||
| } | ||
|
|
||
| /** | ||
| * Adds a transformation at the end of its respective ATransformationPass. | ||
| * @param transformation a {@link GraphTransformation} | ||
| */ | ||
| public void addTransformation(GraphTransformation transformation) { | ||
| switch (transformation.getPhase()) { | ||
| case OBLIGATORY -> PrepareTransformationPass.registerTransformation(transformation); | ||
| case AST_TRANSFORM -> AstTransformationPass.registerTransformation(transformation); | ||
| case CPG_TRANSFORM -> CpgTransformationPass.registerTransformation(transformation); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Registers the given transformations to be applied in the transformation step. | ||
| * @param transformations the transformations | ||
| */ | ||
| public void addTransformations(GraphTransformation[] transformations) { | ||
| Arrays.stream(transformations).forEach(this::addTransformation); | ||
| } | ||
|
|
||
| /** | ||
| * Clears all non-{@link ExecutionPhase#OBLIGATORY} transformations from the pipeline. | ||
| */ | ||
| public void clearTransformations() { | ||
| AstTransformationPass.clearTransformations(); | ||
| CpgTransformationPass.clearTransformations(); | ||
| } | ||
|
|
||
| private <T extends Pass<?>> KClass<T> getKClass(Class<T> javaPassClass) { | ||
| return JvmClassMappingKt.getKotlinClass(javaPassClass); | ||
| } | ||
|
|
||
| /** | ||
| * Sets <code>reorderingEnabled</code>. If true, statements may be reordered. | ||
| * @param enabled value for reorderingEnabled. | ||
| */ | ||
| public void setReorderingEnabled(boolean enabled) { | ||
| this.reorderingEnabled = enabled; | ||
| } | ||
|
|
||
| private void setTokenList(List<Token> tokenList) { | ||
| this.tokenList = tokenList; | ||
| } | ||
|
|
||
| /* package-private */ TranslationResult translate(Set<File> files) throws ParsingException, InterruptedException { | ||
robinmaisch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| InferenceConfiguration inferenceConfiguration = InferenceConfiguration.builder().guessCastExpressions(true).inferRecords(true) | ||
| .inferDfgForUnresolvedCalls(true).build(); | ||
|
|
||
| TranslationResult translationResult; | ||
| TokenizationPass.Companion.setCallback(CpgAdapter.this::setTokenList); | ||
| try { | ||
| TranslationConfiguration.Builder configBuilder = new TranslationConfiguration.Builder().inferenceConfiguration(inferenceConfiguration) | ||
| .sourceLocations(files.toArray(new File[] {})).registerLanguage(new JavaLanguage()); | ||
|
|
||
| List<Class<? extends Pass<?>>> passClasses = new ArrayList<>(List.of(TypeResolver.class, TypeHierarchyResolver.class, | ||
| ImportResolver.class, SymbolResolver.class, PrepareTransformationPass.class, FixAstPass.class, DynamicInvokeResolver.class, | ||
| FilenameMapper.class, AstTransformationPass.class, EvaluationOrderGraphPass.class, // creates | ||
| // EOG | ||
| DfgSortPass.class, CpgTransformationPass.class, TokenizationPass.class)); | ||
|
|
||
| if (!reorderingEnabled) | ||
| passClasses.remove(DfgSortPass.class); | ||
|
|
||
| for (Class<? extends Pass<?>> passClass : passClasses) { | ||
| configBuilder.registerPass(getKClass(passClass)); | ||
| } | ||
|
|
||
| translationResult = TranslationManager.builder().config(configBuilder.build()).build().analyze().get(); | ||
|
|
||
| } catch (InterruptedException e) { | ||
| throw e; | ||
robinmaisch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } catch (ExecutionException | ConfigurationException e) { | ||
| throw new ParsingException(List.copyOf(files).getFirst(), e); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why the copy when only one element is passed?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because any one element can not be gotten from the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
| } | ||
| return translationResult; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| package de.jplag.java_cpg; | ||
|
|
||
| import static de.jplag.java_cpg.transformation.TransformationRepository.*; | ||
|
|
||
| import java.io.File; | ||
| import java.util.List; | ||
| import java.util.Set; | ||
|
|
||
| import org.kohsuke.MetaInfServices; | ||
|
|
||
| import de.jplag.Language; | ||
| import de.jplag.ParsingException; | ||
| import de.jplag.Token; | ||
| import de.jplag.java_cpg.transformation.GraphTransformation; | ||
|
|
||
| /** | ||
| * This class represents the frond end of the CPG module of JPlag. | ||
| */ | ||
| @MetaInfServices(de.jplag.Language.class) | ||
| public class JavaCpgLanguage implements Language { | ||
| private static final int DEFAULT_MINIMUM_TOKEN_MATCH = 9; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could take the dafault mtm of the java module instead of setting it to 9
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is possible, but it would require a module dependency on the Java frontend.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, that would not be justified. |
||
| private static final String[] FILE_EXTENSIONS = {".java"}; | ||
| private static final String NAME = "Java Code Property Graph module"; | ||
| private static final String IDENTIFIER = "java-cpg"; | ||
| private final CpgAdapter cpgAdapter; | ||
|
|
||
| /** | ||
| * Creates a new {@link JavaCpgLanguage}. | ||
| */ | ||
| public JavaCpgLanguage() { | ||
| this.cpgAdapter = new CpgAdapter(allTransformations()); | ||
| } | ||
|
|
||
| /** | ||
| * Adds the given {@link GraphTransformation} to the list to apply to the submissions. | ||
| * @param transformation the transformation | ||
| */ | ||
| public void addTransformation(GraphTransformation transformation) { | ||
| this.cpgAdapter.addTransformation(transformation); | ||
| } | ||
|
|
||
| /** | ||
| * Adds the given {@link GraphTransformation}s to the list to apply to the submissions. | ||
| * @param transformations the transformations | ||
| */ | ||
| public void addTransformations(GraphTransformation[] transformations) { | ||
robinmaisch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| this.cpgAdapter.addTransformations(transformations); | ||
| } | ||
|
|
||
| @Override | ||
| public String getIdentifier() { | ||
| return IDENTIFIER; | ||
| } | ||
|
|
||
| @Override | ||
| public String getName() { | ||
| return NAME; | ||
| } | ||
|
|
||
| @Override | ||
| public int minimumTokenMatch() { | ||
| return DEFAULT_MINIMUM_TOKEN_MATCH; | ||
| } | ||
|
|
||
| @Override | ||
| public List<Token> parse(Set<File> files, boolean normalize) throws ParsingException { | ||
| try { | ||
| return cpgAdapter.adapt(files, normalize); | ||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| return List.of(); | ||
|
Comment on lines
+70
to
+71
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure if returning empty is a good idea here. The user would not understand why there is noting parsed. Why not throw a parsing exception (see method signature)? Also: Can you catch the InterruptedException earlier? Directly in the adapter?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, I'll catch it in the CpgAdapter. |
||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public boolean requiresCoreNormalization() { | ||
| return false; | ||
| } | ||
|
|
||
| /** | ||
| * Resets the set of transformations to the obligatory transformations only. | ||
| */ | ||
| public void resetTransformations() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You have a lot of methods here for the state management of the adapter. Exposing that via the language interface leads you to have a lot of methods that just delegate to the adapter. We should discuss this, maybe there is a better option? Like, two constructors, one with a default adapter and one where you pass the pre-configured adapter? That would remove a lot of complexity here... |
||
| this.cpgAdapter.clearTransformations(); | ||
| this.cpgAdapter.addTransformations(this.obligatoryTransformations()); | ||
| this.cpgAdapter.addTransformations(this.standardTransformations()); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the set of transformations required to ensure that the tokenization works properly. | ||
| * @return the array of obligatory transformations | ||
| */ | ||
| private GraphTransformation[] obligatoryTransformations() { | ||
| return new GraphTransformation[] {wrapThenStatement, wrapElseStatement, wrapForStatement, wrapWhileStatement, wrapDoStatement}; | ||
| } | ||
|
|
||
| /** | ||
| * Returns a set of transformations suggested for use. | ||
| * @return the array of recommended transformations | ||
| */ | ||
| public GraphTransformation[] standardTransformations() { | ||
| return new GraphTransformation[] {removeOptionalOfCall, removeOptionalGetCall, moveConstantToOnlyUsingClass, inlineSingleUseVariable, | ||
| removeLibraryRecord, removeEmptyRecord,}; | ||
| } | ||
|
|
||
| /** | ||
| * Returns a set of all transformations. | ||
| * @return the array of all transformations | ||
| */ | ||
| public GraphTransformation[] allTransformations() { | ||
| return new GraphTransformation[] {ifWithNegatedConditionResolution, forStatementToWhileStatement, removeOptionalOfCall, removeOptionalGetCall, | ||
| removeGetterMethod, moveConstantToOnlyUsingClass, inlineSingleUseConstant, inlineSingleUseVariable, removeEmptyDeclarationStatement, | ||
| removeImplicitStandardConstructor, removeLibraryRecord, removeLibraryField, removeEmptyConstructor, removeUnsupportedConstructor, | ||
| removeUnsupportedMethod, removeEmptyRecord,}; | ||
| } | ||
|
Comment on lines
+92
to
+115
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could also be provided by a separate class to separate the concerns, for example a dedicated utility class? |
||
|
|
||
| @Override | ||
| public String[] suffixes() { | ||
| return FILE_EXTENSIONS; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean supportsNormalization() { | ||
| return true; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.