Skip to content

Commit

Permalink
Merge pull request #205 from Fraunhofer-AISEC/samuel/135-remove-dummies
Browse files Browse the repository at this point in the history
Make dummy creation for calls consistent
  • Loading branch information
konradweiss committed Aug 27, 2020
2 parents 88ca30a + 9a368d2 commit 20d5761
Show file tree
Hide file tree
Showing 22 changed files with 460 additions and 184 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

Expand Down Expand Up @@ -302,8 +304,7 @@ public String recoverTypeFromUnsolvedException(Throwable ex) {
qualifier = ((UnsolvedSymbolException) ex.getCause()).getName();
}
// this comes from the Javaparser!
if (qualifier.startsWith("We are unable to find the value declaration corresponding to")
|| qualifier.startsWith("Solving ")) {
if (qualifier.startsWith("We are unable to find") || qualifier.startsWith("Solving ")) {
return null;
}
String fromImport = getQualifiedNameFromImports(qualifier);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,12 +557,12 @@ public SwitchStatement handleSwitchStatement(Statement stmt) {

private ExplicitConstructorInvocation handleExplicitConstructorInvocation(Statement stmt) {
ExplicitConstructorInvocationStmt eciStatement = stmt.asExplicitConstructorInvocationStmt();
String containingClass;
try {
containingClass = eciStatement.resolve().declaringType().getQualifiedName();
} catch (RuntimeException | NoClassDefFoundError e) {
containingClass = lang.recoverTypeFromUnsolvedException(e);
// base can be null here
String containingClass = "";
RecordDeclaration currentRecord = lang.getScopeManager().getCurrentRecord();
if (currentRecord == null) {
log.error("Explicit constructor invocation has to be located inside a record declaration!");
} else {
containingClass = currentRecord.getName();
}

ExplicitConstructorInvocation node =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
package de.fraunhofer.aisec.cpg.graph;

import de.fraunhofer.aisec.cpg.graph.type.TypeParser;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
* The declaration of a constructor within a {@link RecordDeclaration}. Is it essentially a special
Expand All @@ -47,11 +48,6 @@ public static ConstructorDeclaration from(MethodDeclaration methodDeclaration) {
NodeBuilder.newConstructorDeclaration(
methodDeclaration.getName(), methodDeclaration.getCode(), recordDeclaration);

if (recordDeclaration != null) {
// constructors always have implicitly the return type of their class
c.setType(TypeParser.createFrom(recordDeclaration.getName(), true));
}

c.setBody(methodDeclaration.getBody());
c.setLocation(methodDeclaration.getLocation());
c.setParameters(methodDeclaration.getParameters());
Expand All @@ -66,4 +62,13 @@ public static ConstructorDeclaration from(MethodDeclaration methodDeclaration) {

return c;
}

@Override
public void setRecordDeclaration(@Nullable RecordDeclaration recordDeclaration) {
super.setRecordDeclaration(recordDeclaration);
if (recordDeclaration != null) {
// constructors always have implicitly the return type of their class
setType(TypeParser.createFrom(recordDeclaration.getName(), true));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@

import de.fraunhofer.aisec.cpg.graph.type.Type;
import de.fraunhofer.aisec.cpg.graph.type.UnknownType;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.*;
import java.util.stream.Collectors;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.checkerframework.checker.nullness.qual.Nullable;
Expand Down Expand Up @@ -226,7 +222,11 @@ public String toString() {
return new ToStringBuilder(this, Node.TO_STRING_STYLE)
.appendSuper(super.toString())
.append("type", type)
.append("parameters", parameters.stream().map(ParamVariableDeclaration::getName))
.append(
"parameters",
parameters.stream()
.map(ParamVariableDeclaration::getName)
.collect(Collectors.joining(", ")))
.toString();
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/de/fraunhofer/aisec/cpg/graph/Node.java
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ public void setArgumentIndex(int argumentIndex) {
this.argumentIndex = argumentIndex;
}

/** @deprecated You should rather use {@link #isImplicit()} */
@Deprecated(forRemoval = true)
public boolean isDummy() {
return dummy;
}
Expand Down
157 changes: 66 additions & 91 deletions src/main/java/de/fraunhofer/aisec/cpg/passes/CallResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,10 @@ private void handleNormalCalls(RecordDeclaration curClass, CallExpression call)
.filter(
f -> f.getName().equals(call.getName()) && f.hasSignature(call.getSignature()))
.collect(Collectors.toList());
if (invocationCandidates.isEmpty()) {
invocationCandidates =
List.of(createDummy(null, call.getName(), call.getCode(), false, call.getSignature()));
}

call.setInvokes(invocationCandidates);
} else if (!handlePossibleStaticImport(call, curClass)) {
Expand All @@ -295,13 +299,24 @@ private void handleNormalCalls(RecordDeclaration curClass, CallExpression call)
private void handleMethodCall(RecordDeclaration curClass, CallExpression call) {
Set<Type> possibleContainingTypes = getPossibleContainingTypes(call, curClass);

// Find invokes by type
// Find overridden invokes
List<FunctionDeclaration> invocationCandidates =
call.getInvokes().stream()
.map(f -> getOverridingCandidates(possibleContainingTypes, f))
.flatMap(Collection::stream)
.collect(Collectors.toList());

// Find function targets
if (invocationCandidates.isEmpty() && currentTU != null) {
invocationCandidates =
currentTU.getDeclarations().stream()
.filter(FunctionDeclaration.class::isInstance)
.map(FunctionDeclaration.class::cast)
.filter(
f -> f.getName().equals(call.getName()) && f.hasSignature(call.getSignature()))
.collect(Collectors.toList());
}

// Find invokes by supertypes
if (invocationCandidates.isEmpty()) {
String[] nameParts = call.getName().split("\\.");
Expand All @@ -321,6 +336,14 @@ private void handleMethodCall(RecordDeclaration curClass, CallExpression call) {
&& !(call instanceof MemberCallExpression || call instanceof StaticCallExpression)) {
call.setBase(curClass.getThis());
}

if (invocationCandidates.isEmpty()) {
possibleContainingTypes.stream()
.map(t -> recordMap.get(t.getTypeName()))
.filter(Objects::nonNull)
.map(r -> createDummy(r, call.getName(), call.getCode(), false, call.getSignature()))
.forEach(invocationCandidates::add);
}
call.setInvokes(invocationCandidates);
}

Expand All @@ -332,56 +355,40 @@ private void resolveConstructExpression(ConstructExpression constructExpression)

if (record != null && record.getCode() != null && !record.getCode().isEmpty()) {
ConstructorDeclaration constructor = getConstructorDeclaration(signature, record);
if (constructor != null) {
constructExpression.setConstructor(constructor);
} else {
LOGGER.warn(
"Unexpected: Could not find constructor for {} with signature {}",
record.getName(),
signature);
}
constructExpression.setConstructor(constructor);
}
}

private void handleFunctionPointerCall(CallExpression call, Node pointer) {
if (!(pointer instanceof HasType
&& ((HasType) pointer).getType() instanceof FunctionPointerType)) {
LOGGER.error("Can't handle a function pointer call without function pointer type");
return;
}
FunctionPointerType pointerType = (FunctionPointerType) ((HasType) pointer).getType();
List<FunctionDeclaration> invocationCandidates = new ArrayList<>();
Deque<Node> worklist = new ArrayDeque<>();
Set<Node> seen = Collections.newSetFromMap(new IdentityHashMap<>());
worklist.push(pointer);
DeclaredReferenceExpression finalReference = null;
while (!worklist.isEmpty()) {
Node curr = worklist.pop();
if (!seen.add(curr)) {
continue;
}
if (curr instanceof FunctionDeclaration) {
if (((FunctionDeclaration) curr).hasSignature(call.getSignature())) {
FunctionDeclaration f = (FunctionDeclaration) curr;
// Even if it is a function declaration, the dataflow might just come from a situation
// where the target of a fptr is passed through via a return value. Keep searching if
// return type or signature don't match
if (TypeManager.getInstance().isSupertypeOf(pointerType.getReturnType(), f.getType())
&& f.hasSignature(pointerType.getParameters())) {
invocationCandidates.add((FunctionDeclaration) curr);
} else if (curr.isImplicit()) {
// unknown function, so even if its signature does not fit, we need to make a new
// dummy that does match
if (((FunctionDeclaration) curr).hasSignature(call.getSignature())) {
invocationCandidates.add((FunctionDeclaration) curr);
// refine the referral pointer
if (finalReference != null && finalReference.getRefersTo().contains(curr)) {
finalReference.setRefersTo((ValueDeclaration) curr);
}
} else {
FunctionDeclaration dummy =
createDummyWithMatchingSignature((FunctionDeclaration) curr, call.getSignature());
invocationCandidates.add(dummy);
// redirect the referral pointer
if (finalReference != null && finalReference.getRefersTo().contains(curr)) {
finalReference.setRefersTo(dummy);
}
}
// We have found a target. Don't follow this path any further, but still continue the
// other paths that might be left, as we could have several potential targets at runtime
continue;
}
} else {
if (curr instanceof DeclaredReferenceExpression) {
finalReference = (DeclaredReferenceExpression) curr;
}
curr.getPrevDFG().forEach(worklist::push);
}
curr.getPrevDFG().forEach(worklist::push);
}
call.setInvokes(invocationCandidates);
}
Expand All @@ -394,9 +401,7 @@ private void resolveExplicitConstructorInvocation(ExplicitConstructorInvocation
if (record != null) {
ConstructorDeclaration constructor = getConstructorDeclaration(signature, record);
ArrayList<FunctionDeclaration> invokes = new ArrayList<>();
if (constructor != null) {
invokes.add(constructor);
}
invokes.add(constructor);
eci.setInvokes(invokes);
}
}
Expand Down Expand Up @@ -463,78 +468,49 @@ private void generateStaticImportDummies(
}
}

private Optional<FunctionDeclaration> checkExistingDummies(
FunctionDeclaration template, List<Type> signature) {
if (template instanceof MethodDeclaration
&& ((MethodDeclaration) template).getRecordDeclaration() != null) {
return ((MethodDeclaration) template)
.getRecordDeclaration().getMethods().stream()
.filter(m -> m.getName().equals(template.getName()) && m.hasSignature(signature))
.map(FunctionDeclaration.class::cast)
.findFirst();
} else {
if (currentTU == null) {
LOGGER.error(
"No current translation unit when trying to find matching dummy for {}", template);
return Optional.empty();
}
return currentTU.getDeclarations().stream()
.filter(FunctionDeclaration.class::isInstance)
.map(FunctionDeclaration.class::cast)
.filter(f -> f.getName().equals(template.getName()) && f.hasSignature(signature))
.findFirst();
}
}

private FunctionDeclaration createDummyWithMatchingSignature(
FunctionDeclaration template, List<Type> signature) {
Optional<FunctionDeclaration> existing = checkExistingDummies(template, signature);
if (existing.isPresent()) {
return existing.get();
}
@NonNull
private FunctionDeclaration createDummy(
RecordDeclaration containingRecord,
String name,
String code,
boolean isStatic,
List<Type> signature) {

List<ParamVariableDeclaration> parameters = Util.createParameters(signature);
if (template instanceof MethodDeclaration) {
RecordDeclaration containingRecord = ((MethodDeclaration) template).getRecordDeclaration();
if (containingRecord != null) {
MethodDeclaration dummy =
NodeBuilder.newMethodDeclaration(
template.getName(),
template.getCode(),
((MethodDeclaration) template).isStatic(),
containingRecord);
NodeBuilder.newMethodDeclaration(name, code, isStatic, containingRecord);
dummy.setImplicit(true);
dummy.setParameters(parameters);

if (containingRecord == null) {
// not inside a class, lets put it inside the translation unit
if (currentTU == null) {
LOGGER.error(
"No current translation unit when trying to generate method dummy {}",
dummy.getName());
} else {
currentTU.getDeclarations().add(dummy);
}
} else {
containingRecord.getMethods().add(dummy);
}
containingRecord.getMethods().add(dummy);
return dummy;
} else {
// function declaration, not inside a class
FunctionDeclaration dummy =
NodeBuilder.newFunctionDeclaration(template.getName(), template.getCode());
FunctionDeclaration dummy = NodeBuilder.newFunctionDeclaration(name, code);
dummy.setParameters(parameters);
dummy.setImplicit(true);
if (currentTU == null) {
LOGGER.error(
"No current translation unit when trying to generate function dummy {}",
dummy.getName());
} else {
currentTU.getDeclarations().add(dummy);
currentTU.add(dummy);
}
return dummy;
}
}

private ConstructorDeclaration createConstructorDummy(
@NonNull RecordDeclaration containingRecord, List<Type> signature) {
ConstructorDeclaration dummy =
NodeBuilder.newConstructorDeclaration(containingRecord.getName(), "", containingRecord);
dummy.setImplicit(true);
dummy.setParameters(Util.createParameters(signature));
containingRecord.getConstructors().add(dummy);
return dummy;
}

private Set<Type> getPossibleContainingTypes(Node node, RecordDeclaration curClass) {
Set<Type> possibleTypes = new HashSet<>();
if (node instanceof MemberCallExpression) {
Expand All @@ -551,7 +527,6 @@ private Set<Type> getPossibleContainingTypes(Node node, RecordDeclaration curCla
}
} else if (curClass != null) {
possibleTypes.add(TypeParser.createFrom(curClass.getName(), true));
possibleTypes.addAll(curClass.getSuperTypes());
}
return possibleTypes;
}
Expand Down Expand Up @@ -595,12 +570,12 @@ private Set<FunctionDeclaration> getOverridingCandidates(
.collect(Collectors.toSet());
}

@Nullable
@NonNull
private ConstructorDeclaration getConstructorDeclaration(
List<Type> signature, RecordDeclaration record) {
return record.getConstructors().stream()
.filter(f -> f.hasSignature(signature))
.findFirst()
.orElse(null);
.orElseGet(() -> createConstructorDummy(record, signature));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class JavaExternalTypeHierarchyResolver extends Pass {
@Override
public void accept(TranslationResult translationResult) {
// Run only for Java.
if (JavaLanguageFrontend.class.equals(this.lang.getClass())) {
if (this.lang instanceof JavaLanguageFrontend) {
TypeSolver resolver = ((JavaLanguageFrontend) this.lang).getNativeTypeResolver();
TypeManager tm = TypeManager.getInstance();

Expand Down

0 comments on commit 20d5761

Please sign in to comment.