Skip to content

Commit

Permalink
Merge pull request #249 from Fraunhofer-AISEC/java-annotations
Browse files Browse the repository at this point in the history
Java annotations
  • Loading branch information
konradweiss authored Oct 15, 2020
2 parents b655243 + f96f359 commit 0d674b6
Show file tree
Hide file tree
Showing 9 changed files with 254 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@
package de.fraunhofer.aisec.cpg.frontends.cpp;

import static de.fraunhofer.aisec.cpg.graph.NodeBuilder.newAnnotation;
import static de.fraunhofer.aisec.cpg.graph.NodeBuilder.newAnnotationMember;
import static de.fraunhofer.aisec.cpg.graph.NodeBuilder.newDeclaredReferenceExpression;
import static de.fraunhofer.aisec.cpg.graph.NodeBuilder.newLiteral;

import de.fraunhofer.aisec.cpg.TranslationConfiguration;
import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend;
import de.fraunhofer.aisec.cpg.frontends.TranslationException;
import de.fraunhofer.aisec.cpg.graph.Annotation;
import de.fraunhofer.aisec.cpg.graph.AnnotationMember;
import de.fraunhofer.aisec.cpg.graph.Node;
import de.fraunhofer.aisec.cpg.graph.TypeManager;
import de.fraunhofer.aisec.cpg.graph.declarations.Declaration;
Expand All @@ -58,7 +60,15 @@
import java.util.stream.Collectors;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.eclipse.cdt.core.dom.ast.*;
import org.eclipse.cdt.core.dom.ast.IASTAttribute;
import org.eclipse.cdt.core.dom.ast.IASTAttributeOwner;
import org.eclipse.cdt.core.dom.ast.IASTComment;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTToken;
import org.eclipse.cdt.core.dom.ast.IASTTokenList;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.gnu.cpp.GPPLanguage;
import org.eclipse.cdt.core.index.IIndexFileLocation;
import org.eclipse.cdt.core.model.ILanguage;
Expand Down Expand Up @@ -393,9 +403,10 @@ private List<Annotation> handleAttributes(IASTAttributeOwner owner) {

// go over the parameters
if (attribute.getArgumentClause() instanceof IASTTokenList) {
List<Expression> values = handleTokenList((IASTTokenList) attribute.getArgumentClause());
List<AnnotationMember> members =
handleTokenList((IASTTokenList) attribute.getArgumentClause());

annotation.setValues(values);
annotation.setMembers(members);
}

list.add(annotation);
Expand All @@ -404,10 +415,11 @@ private List<Annotation> handleAttributes(IASTAttributeOwner owner) {
return list;
}

private List<Expression> handleTokenList(IASTTokenList tokenList) {
List<Expression> list = new ArrayList<>();
private List<AnnotationMember> handleTokenList(IASTTokenList tokenList) {
List<AnnotationMember> list = new ArrayList<>();

for (IASTToken token : tokenList.getTokens()) {
// skip commas and such
if (token.getTokenType() == 6) {
continue;
}
Expand All @@ -418,22 +430,32 @@ private List<Expression> handleTokenList(IASTTokenList tokenList) {
return list;
}

private Expression handleToken(IASTToken token) {
private AnnotationMember handleToken(IASTToken token) {
String code = new String(token.getTokenCharImage());

Expression expression;
switch (token.getTokenType()) {
case 1:
return newDeclaredReferenceExpression(code, UnknownType.getUnknownType(), code);
// a variable
expression = newDeclaredReferenceExpression(code, UnknownType.getUnknownType(), code);
break;
case 2:
return newLiteral(Integer.parseInt(code), TypeParser.createFrom("int", true), code);
// an integer
expression = newLiteral(Integer.parseInt(code), TypeParser.createFrom("int", true), code);
break;
case 130:
return newLiteral(
code.length() >= 2 ? code.substring(1, code.length() - 1) : "",
TypeParser.createFrom("const char*", false),
code);
// a string
expression =
newLiteral(
code.length() >= 2 ? code.substring(1, code.length() - 1) : "",
TypeParser.createFrom("const char*", false),
code);
break;
default:
return newLiteral(code, TypeParser.createFrom("const char*", false), code);
expression = newLiteral(code, TypeParser.createFrom("const char*", false), code);
}

return newAnnotationMember(null, expression, code);
}

private Field getField(Class<?> type, String fieldName) throws NoSuchFieldException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ private static void addImplicitReturn(BlockStmt body) {
addImplicitReturn(body);

declaration.setBody(this.lang.getStatementHandler().handle(body));

lang.processAnnotations(declaration, constructorDecl);

lang.getScopeManager().leaveScope(declaration);
return declaration;
}
Expand Down Expand Up @@ -192,6 +195,9 @@ public de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration handleMethod
addImplicitReturn(body);

functionDeclaration.setBody(this.lang.getStatementHandler().handle(body));

lang.processAnnotations(functionDeclaration, methodDecl);

lang.getScopeManager().leaveScope(functionDeclaration);
return functionDeclaration;
}
Expand Down Expand Up @@ -272,6 +278,9 @@ public RecordDeclaration handleClassOrInterfaceDeclaration(
recordDeclaration.getConstructors().add(constructorDeclaration);
lang.getScopeManager().addDeclaration(constructorDeclaration);
}

lang.processAnnotations(recordDeclaration, classInterDecl);

lang.getScopeManager().leaveScope(recordDeclaration);
return recordDeclaration;
}
Expand Down Expand Up @@ -317,6 +326,8 @@ public de.fraunhofer.aisec.cpg.graph.declarations.FieldDeclaration handleFieldDe
false);
lang.getScopeManager().addDeclaration(fieldDeclaration);

this.lang.processAnnotations(fieldDeclaration, fieldDecl);

return fieldDeclaration;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,24 @@

package de.fraunhofer.aisec.cpg.frontends.java;

import static de.fraunhofer.aisec.cpg.graph.NodeBuilder.newAnnotation;
import static de.fraunhofer.aisec.cpg.graph.NodeBuilder.newAnnotationMember;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseResult;
import com.github.javaparser.ParserConfiguration;
import com.github.javaparser.Range;
import com.github.javaparser.TokenRange;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.ImportDeclaration;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.Node.Parsedness;
import com.github.javaparser.ast.PackageDeclaration;
import com.github.javaparser.ast.body.TypeDeclaration;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.MemberValuePair;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations;
import com.github.javaparser.ast.nodeTypes.NodeWithType;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.Type;
Expand All @@ -54,6 +58,9 @@
import de.fraunhofer.aisec.cpg.TranslationConfiguration;
import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend;
import de.fraunhofer.aisec.cpg.frontends.TranslationException;
import de.fraunhofer.aisec.cpg.graph.Annotation;
import de.fraunhofer.aisec.cpg.graph.AnnotationMember;
import de.fraunhofer.aisec.cpg.graph.Node;
import de.fraunhofer.aisec.cpg.graph.NodeBuilder;
import de.fraunhofer.aisec.cpg.graph.TypeManager;
import de.fraunhofer.aisec.cpg.graph.declarations.IncludeDeclaration;
Expand Down Expand Up @@ -200,8 +207,8 @@ protected CompilationUnit parse(File file, JavaParser parser)

@Override
public <T> String getCodeFromRawNode(T astNode) {
if (astNode instanceof Node) {
Node node = (Node) astNode;
if (astNode instanceof com.github.javaparser.ast.Node) {
var node = (com.github.javaparser.ast.Node) astNode;
Optional<TokenRange> optional = node.getTokenRange();
if (optional.isPresent()) {
return optional.get().toString();
Expand All @@ -213,8 +220,8 @@ public <T> String getCodeFromRawNode(T astNode) {
@Override
@Nullable
public <T> PhysicalLocation getLocationFromRawNode(T astNode) {
if (astNode instanceof Node) {
Node node = (Node) astNode;
if (astNode instanceof com.github.javaparser.ast.Node) {
var node = (com.github.javaparser.ast.Node) astNode;

// find compilation unit of node
CompilationUnit cu = node.findCompilationUnit().orElse(null);
Expand Down Expand Up @@ -430,7 +437,7 @@ public void cleanup() {
@Override
public <S, T> void setComment(S s, T ctx) {
if (ctx instanceof Node && s instanceof de.fraunhofer.aisec.cpg.graph.Node) {
Node node = (Node) ctx;
var node = (com.github.javaparser.ast.Node) ctx;
de.fraunhofer.aisec.cpg.graph.Node cpgNode = (de.fraunhofer.aisec.cpg.graph.Node) s;
node.getComment().ifPresent(comment -> cpgNode.setComment(comment.getContent()));
// TODO: handle orphanComments?
Expand All @@ -456,4 +463,47 @@ public CompilationUnit getContext() {
public CombinedTypeSolver getNativeTypeResolver() {
return this.internalTypeSolver;
}

/**
* Processes Java annotations.
*
* @param node the node
* @param owner the AST owner node
*/
public void processAnnotations(@NonNull Node node, NodeWithAnnotations<?> owner) {
if (this.config.processAnnotations) {
node.addAnnotations(handleAnnotations(owner));
}
}

private List<Annotation> handleAnnotations(NodeWithAnnotations<?> owner) {
var list = new ArrayList<Annotation>();

for (var expr : owner.getAnnotations()) {
var annotation = newAnnotation(expr.getNameAsString(), getCodeFromRawNode(expr));

var members = new ArrayList<AnnotationMember>();

for (var child : expr.getChildNodes()) {
if (child instanceof MemberValuePair) {
var pair = (MemberValuePair) child;

var member =
newAnnotationMember(
pair.getNameAsString(),
(de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression)
expressionHandler.handle(pair.getValue()),
getCodeFromRawNode(pair));

members.add(member);
}

annotation.setMembers(members);
}

list.add(annotation);
}

return list;
}
}
23 changes: 16 additions & 7 deletions src/main/java/de/fraunhofer/aisec/cpg/graph/Annotation.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,27 @@
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression;
import java.util.List;
import java.util.Objects;
import javax.annotation.Nullable;

public class Annotation extends Node {

// should be extended later into annotation members
private List<Expression> values;
private List<AnnotationMember> members;

public List<Expression> getValues() {
return values;
public List<AnnotationMember> getMembers() {
return members;
}

public void setValues(List<Expression> values) {
this.values = values;
public void setMembers(List<AnnotationMember> members) {
this.members = members;
}

@Nullable
public Expression getValueForName(String name) {
return members.stream()
.filter(member -> Objects.equals(member.name, name))
.map(AnnotationMember::getValue)
.findAny()
.orElse(null);
}

@Override
Expand All @@ -53,7 +62,7 @@ public boolean equals(Object o) {
}

Annotation that = (Annotation) o;
return super.equals(that) && Objects.equals(values, that.values);
return super.equals(that) && Objects.equals(members, that.members);
}

@Override
Expand Down
42 changes: 42 additions & 0 deletions src/main/java/de/fraunhofer/aisec/cpg/graph/AnnotationMember.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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.graph;

import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression;

public class AnnotationMember extends Node {

public Expression getValue() {
return value;
}

public void setValue(Expression value) {
this.value = value;
}

private Expression value;
}
10 changes: 10 additions & 0 deletions src/main/java/de/fraunhofer/aisec/cpg/graph/NodeBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -695,4 +695,14 @@ public static Annotation newAnnotation(String name, @NonNull String code) {

return annotation;
}

public static AnnotationMember newAnnotationMember(
String name, Expression value, @NonNull String code) {
var member = new AnnotationMember();
member.setName(name);
member.setValue(value);
member.setCode(code);

return member;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1225,8 +1225,8 @@ void testAttributes() throws Exception {
assertNotNull(annotation);

assertEquals("property_attribute", annotation.getName());
assertEquals(3, annotation.getValues().size());
assertEquals("a", ((Literal<String>) annotation.getValues().get(0)).getValue());
assertEquals(3, annotation.getMembers().size());
assertEquals("a", ((Literal<String>) annotation.getMembers().get(0).getValue()).getValue());

FieldDeclaration b =
someClass.getFields().stream().filter(f -> f.getName().equals("b")).findAny().orElse(null);
Expand All @@ -1236,10 +1236,10 @@ void testAttributes() throws Exception {
assertNotNull(annotation);

assertEquals("property_attribute", annotation.getName());
assertEquals(1, annotation.getValues().size());
assertEquals(1, annotation.getMembers().size());
assertEquals(
"SomeCategory, SomeOtherThing",
((Literal<String>) annotation.getValues().get(0)).getValue());
((Literal<String>) annotation.getMembers().get(0).getValue()).getValue());
}

@Test
Expand Down
Loading

0 comments on commit 0d674b6

Please sign in to comment.