diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/Injector.java b/injector/src/main/java/edu/ucr/cs/riple/injector/Injector.java index bd6be6a17..331fde812 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/Injector.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/Injector.java @@ -29,6 +29,7 @@ import com.github.javaparser.ast.ImportDeclaration; import edu.ucr.cs.riple.injector.changes.AddAnnotation; import edu.ucr.cs.riple.injector.changes.Change; +import edu.ucr.cs.riple.injector.changes.ChangeVisitor; import edu.ucr.cs.riple.injector.changes.RemoveAnnotation; import edu.ucr.cs.riple.injector.modifications.Modification; import edu.ucr.cs.riple.injector.offsets.FileOffsetStore; @@ -62,11 +63,12 @@ public Set start(Set changes) { } catch (IOException exception) { return; } + ChangeVisitor visitor = new ChangeVisitor(tree); Set modifications = new HashSet<>(); Set imports = new HashSet<>(); for (Change change : changeList) { try { - Modification modification = change.translate(tree); + Modification modification = visitor.computeModification(change); if (modification != null) { modifications.add(modification); if (change instanceof AddAnnotation) { diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/Change.java b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/Change.java index 459c8b9bf..d63e3a968 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/Change.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/Change.java @@ -22,7 +22,6 @@ package edu.ucr.cs.riple.injector.changes; -import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations; import com.github.javaparser.ast.nodeTypes.NodeWithRange; import edu.ucr.cs.riple.injector.Helper; @@ -48,18 +47,6 @@ public Change(Location location, String annotation) { this.annotationSimpleName = Helper.simpleName(annotation); } - /** - * Translate the change to a text modification in the source file. - * - * @param tree Compilation unit tree instance. - * @return A text modification instance if the translation is successful, otherwise {@code null} - * will be returned. - */ - @Nullable - public Modification translate(CompilationUnit tree) { - return this.location.apply(tree, this); - } - /** * Visits the given node and translates the change to a text modification. * diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/ChangeVisitor.java b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/ChangeVisitor.java new file mode 100644 index 000000000..0115287f8 --- /dev/null +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/ChangeVisitor.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2023 University of California, Riverside. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package edu.ucr.cs.riple.injector.changes; + +import static edu.ucr.cs.riple.injector.location.OnClass.isAnonymousClassFlatName; + +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.body.BodyDeclaration; +import com.github.javaparser.ast.body.Parameter; +import com.github.javaparser.ast.body.VariableDeclarator; +import com.github.javaparser.utils.Pair; +import edu.ucr.cs.riple.injector.Helper; +import edu.ucr.cs.riple.injector.exceptions.TargetClassNotFound; +import edu.ucr.cs.riple.injector.location.LocationVisitor; +import edu.ucr.cs.riple.injector.location.OnClass; +import edu.ucr.cs.riple.injector.location.OnField; +import edu.ucr.cs.riple.injector.location.OnMethod; +import edu.ucr.cs.riple.injector.location.OnParameter; +import edu.ucr.cs.riple.injector.modifications.Modification; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +import javax.annotation.Nullable; + +/** + * A visitor for computing the required {@link Modification} to a compilation unit on a specified + * location for the requested change. + */ +public class ChangeVisitor + implements LocationVisitor>, Change>> { + + /** Compilation unit which the changes will be applied. */ + private final CompilationUnit cu; + + public ChangeVisitor(CompilationUnit cu) { + this.cu = cu; + } + + @Override + @Nullable + public Modification visitMethod( + OnMethod onMethod, Pair>, Change> pair) { + final AtomicReference ans = new AtomicReference<>(); + final NodeList> members = pair.a; + final Change change = pair.b; + members.forEach( + bodyDeclaration -> + bodyDeclaration.ifCallableDeclaration( + callableDeclaration -> { + if (ans.get() != null) { + // already found the member. + return; + } + if (onMethod.matchesCallableDeclaration(callableDeclaration)) { + ans.set(change.visit(callableDeclaration)); + } + })); + if (ans.get() == null) { + members.forEach( + bodyDeclaration -> + bodyDeclaration.ifAnnotationMemberDeclaration( + annotationMemberDeclaration -> { + if (annotationMemberDeclaration + .getNameAsString() + .equals(Helper.extractCallableName(onMethod.method))) { + ans.set(change.visit(annotationMemberDeclaration)); + } + })); + } + return ans.get(); + } + + @Override + @Nullable + public Modification visitField(OnField onField, Pair>, Change> pair) { + final AtomicReference ans = new AtomicReference<>(); + final NodeList> members = pair.a; + final Change change = pair.b; + members.forEach( + bodyDeclaration -> + bodyDeclaration.ifFieldDeclaration( + fieldDeclaration -> { + if (ans.get() != null) { + // already found the member. + return; + } + NodeList vars = + fieldDeclaration.asFieldDeclaration().getVariables(); + for (VariableDeclarator v : vars) { + if (onField.variables.contains(v.getName().toString())) { + ans.set(change.visit(fieldDeclaration)); + break; + } + } + })); + return ans.get(); + } + + @Override + @Nullable + public Modification visitParameter( + OnParameter onParameter, Pair>, Change> pair) { + final AtomicReference ans = new AtomicReference<>(); + final NodeList> members = pair.a; + final Change change = pair.b; + members.forEach( + bodyDeclaration -> + bodyDeclaration.ifCallableDeclaration( + callableDeclaration -> { + if (ans.get() != null) { + // already found the member. + return; + } + if (onParameter.matchesCallableDeclaration(callableDeclaration)) { + NodeList params = callableDeclaration.getParameters(); + if (onParameter.index < params.size()) { + if (params.get(onParameter.index) != null) { + Node param = params.get(onParameter.index); + if (param instanceof Parameter) { + ans.set(change.visit((Parameter) param)); + } + } + } + } + })); + return ans.get(); + } + + @Override + @Nullable + public Modification visitClass(OnClass onClass, Pair>, Change> pair) { + final NodeList> members = pair.a; + final Change change = pair.b; + if (isAnonymousClassFlatName(change.location.clazz)) { + return null; + } + // Get the enclosing class of the members + Optional optionalClass = members.getParentNode(); + if (optionalClass.isEmpty() || !(optionalClass.get() instanceof BodyDeclaration)) { + return null; + } + return change.visit(((BodyDeclaration) optionalClass.get())); + } + + /** + * Computes the required {@link Modification} that should be applied to the compilation unit for + * the given change. + * + * @param change the change to apply. + * @return the modification that should be applied. + */ + @Nullable + public Modification computeModification(Change change) { + NodeList> members; + try { + members = Helper.getTypeDeclarationMembersByFlatName(cu, change.location.clazz); + if (members == null) { + return null; + } + return change.location.accept(this, new Pair<>(members, change)); + } catch (TargetClassNotFound notFound) { + System.err.println(notFound.getMessage()); + return null; + } + } +} diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/Location.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/Location.java index 5bab029fe..bf7a79631 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/location/Location.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/Location.java @@ -24,15 +24,9 @@ package edu.ucr.cs.riple.injector.location; -import com.github.javaparser.ast.CompilationUnit; -import com.github.javaparser.ast.NodeList; -import com.github.javaparser.ast.body.BodyDeclaration; import com.google.common.base.Preconditions; import com.google.common.collect.Sets; import edu.ucr.cs.riple.injector.Helper; -import edu.ucr.cs.riple.injector.changes.Change; -import edu.ucr.cs.riple.injector.exceptions.TargetClassNotFound; -import edu.ucr.cs.riple.injector.modifications.Modification; import java.nio.file.Path; import java.util.Arrays; import java.util.Objects; @@ -104,36 +98,6 @@ public static Location createLocationFromArrayInfo(String[] values) { throw new RuntimeException("Cannot reach this statement, values: " + Arrays.toString(values)); } - /** - * Applies the change to the element in this location. - * - * @param members The list of members of the enclosing class of the target element. - * @param change The change to be applied on the target element. - * @return The modification that should be applied on the source file. - */ - @Nullable - protected abstract Modification applyToMember( - NodeList> members, Change change); - - /** - * Applies the change to the target element on the given compilation unit tree. - * - * @param tree CompilationUnit Tree to locate the target element. - * @param change Change to be applied on the target element. - * @return The modification that should be applied on the source file. - */ - @Nullable - public Modification apply(CompilationUnit tree, Change change) { - NodeList> clazz; - try { - clazz = Helper.getTypeDeclarationMembersByFlatName(tree, this.clazz); - } catch (TargetClassNotFound notFound) { - System.err.println(notFound.getMessage()); - return null; - } - return applyToMember(clazz, change); - } - /** * If this location is of kind {@link LocationKind#METHOD}, calls the consumer on the location. * diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnClass.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnClass.java index 3260d1d91..e5cffb497 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnClass.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnClass.java @@ -24,16 +24,9 @@ package edu.ucr.cs.riple.injector.location; -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.NodeList; -import com.github.javaparser.ast.body.BodyDeclaration; import edu.ucr.cs.riple.injector.Helper; -import edu.ucr.cs.riple.injector.changes.Change; -import edu.ucr.cs.riple.injector.modifications.Modification; import java.nio.file.Path; -import java.util.Optional; import java.util.regex.Pattern; -import javax.annotation.Nullable; /** Represents a location for class element. This location is used to apply changes to a class. */ public class OnClass extends Location { @@ -52,20 +45,6 @@ public OnClass(String path, String clazz) { this(Helper.deserializePath(path), clazz); } - @Override - @Nullable - protected Modification applyToMember(NodeList> members, Change change) { - if (isAnonymousClassFlatName(change.location.clazz)) { - return null; - } - // Get the enclosing class of the members - Optional optionalClass = members.getParentNode(); - if (optionalClass.isEmpty() || !(optionalClass.get() instanceof BodyDeclaration)) { - return null; - } - return change.visit(((BodyDeclaration) optionalClass.get())); - } - /** * Checks if flat name is for an anonymous class. * diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnField.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnField.java index ec4380274..9331cfddc 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnField.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnField.java @@ -24,19 +24,12 @@ package edu.ucr.cs.riple.injector.location; -import com.github.javaparser.ast.NodeList; -import com.github.javaparser.ast.body.BodyDeclaration; -import com.github.javaparser.ast.body.VariableDeclarator; import edu.ucr.cs.riple.injector.Helper; -import edu.ucr.cs.riple.injector.changes.Change; -import edu.ucr.cs.riple.injector.modifications.Modification; import java.nio.file.Path; import java.util.Collections; import java.util.Objects; import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import javax.annotation.Nullable; /** * Represents a location for field element. This location is used to apply changes to a class field. @@ -66,30 +59,6 @@ public OnField(String path, String clazz, Set variables) { this(Helper.deserializePath(path), clazz, variables); } - @Override - @Nullable - protected Modification applyToMember(NodeList> members, Change change) { - final AtomicReference ans = new AtomicReference<>(); - members.forEach( - bodyDeclaration -> - bodyDeclaration.ifFieldDeclaration( - fieldDeclaration -> { - if (ans.get() != null) { - // already found the member. - return; - } - NodeList vars = - fieldDeclaration.asFieldDeclaration().getVariables(); - for (VariableDeclarator v : vars) { - if (variables.contains(v.getName().toString())) { - ans.set(change.visit(fieldDeclaration)); - break; - } - } - })); - return ans.get(); - } - @Override public void ifField(Consumer consumer) { consumer.accept(this); diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnMethod.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnMethod.java index 1da79b3a5..f3c2c1cfc 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnMethod.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnMethod.java @@ -24,17 +24,12 @@ package edu.ucr.cs.riple.injector.location; -import com.github.javaparser.ast.NodeList; -import com.github.javaparser.ast.body.BodyDeclaration; +import com.github.javaparser.ast.body.CallableDeclaration; import edu.ucr.cs.riple.injector.Helper; import edu.ucr.cs.riple.injector.SignatureMatcher; -import edu.ucr.cs.riple.injector.changes.Change; -import edu.ucr.cs.riple.injector.modifications.Modification; import java.nio.file.Path; import java.util.Objects; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import javax.annotation.Nullable; /** Represents a location for method element. This location is used to apply changes to a method. */ public class OnMethod extends Location { @@ -57,35 +52,14 @@ public OnMethod(String path, String clazz, String method) { this(Helper.deserializePath(path), clazz, method); } - @Override - @Nullable - protected Modification applyToMember(NodeList> members, Change change) { - final AtomicReference ans = new AtomicReference<>(); - members.forEach( - bodyDeclaration -> - bodyDeclaration.ifCallableDeclaration( - callableDeclaration -> { - if (ans.get() != null) { - // already found the member. - return; - } - if (this.matcher.matchesCallableDeclaration(callableDeclaration)) { - ans.set(change.visit(callableDeclaration)); - } - })); - if (ans.get() == null) { - members.forEach( - bodyDeclaration -> - bodyDeclaration.ifAnnotationMemberDeclaration( - annotationMemberDeclaration -> { - if (annotationMemberDeclaration - .getNameAsString() - .equals(Helper.extractCallableName(method))) { - ans.set(change.visit(annotationMemberDeclaration)); - } - })); - } - return ans.get(); + /** + * Checks if the given method matches the method signature of this location. + * + * @param method method to check. + * @return true, if the given method matches the method signature of this location. + */ + public boolean matchesCallableDeclaration(CallableDeclaration method) { + return this.matcher.matchesCallableDeclaration(method); } @Override diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnParameter.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnParameter.java index 2c10a32ff..d7f14d9dd 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnParameter.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnParameter.java @@ -24,19 +24,12 @@ package edu.ucr.cs.riple.injector.location; -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.NodeList; -import com.github.javaparser.ast.body.BodyDeclaration; -import com.github.javaparser.ast.body.Parameter; +import com.github.javaparser.ast.body.CallableDeclaration; import edu.ucr.cs.riple.injector.Helper; import edu.ucr.cs.riple.injector.SignatureMatcher; -import edu.ucr.cs.riple.injector.changes.Change; -import edu.ucr.cs.riple.injector.modifications.Modification; import java.nio.file.Path; import java.util.Objects; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import javax.annotation.Nullable; /** * Represents a location for parameter element. This location is used to apply changes to a @@ -65,33 +58,6 @@ public OnParameter(String path, String clazz, String method, int index) { this(Helper.deserializePath(path), clazz, method, index); } - @Override - @Nullable - protected Modification applyToMember(NodeList> members, Change change) { - final AtomicReference ans = new AtomicReference<>(); - members.forEach( - bodyDeclaration -> - bodyDeclaration.ifCallableDeclaration( - callableDeclaration -> { - if (ans.get() != null) { - // already found the member. - return; - } - if (matcher.matchesCallableDeclaration(callableDeclaration)) { - NodeList params = callableDeclaration.getParameters(); - if (index < params.size()) { - if (params.get(index) != null) { - Node param = params.get(index); - if (param instanceof Parameter) { - ans.set(change.visit((Parameter) param)); - } - } - } - } - })); - return ans.get(); - } - @Override public void ifParameter(Consumer consumer) { consumer.accept(this); @@ -117,6 +83,16 @@ public boolean equals(Object o) { return super.equals(other) && method.equals(other.method) && index == other.index; } + /** + * Checks if the given method matches the method signature of this location. + * + * @param method method to check. + * @return true if the method matches the method signature of this location. + */ + public boolean matchesCallableDeclaration(CallableDeclaration method) { + return this.matcher.matchesCallableDeclaration(method); + } + @Override public R accept(LocationVisitor v, P p) { return v.visitParameter(this, p);