From ae85f9bf86252952ee8cb89d5c29cb4f1b524f05 Mon Sep 17 00:00:00 2001 From: nimakarimipour Date: Tue, 4 Apr 2023 10:57:27 -0700 Subject: [PATCH 01/35] add visitor architecture --- .../cs/riple/injector/location/Location.java | 11 +++ .../injector/location/LocationVisitor.java | 69 +++++++++++++++++++ .../cs/riple/injector/location/OnClass.java | 5 ++ .../cs/riple/injector/location/OnField.java | 5 ++ .../cs/riple/injector/location/OnMethod.java | 5 ++ .../riple/injector/location/OnParameter.java | 5 ++ 6 files changed, 100 insertions(+) create mode 100644 injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationVisitor.java 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 d449cfd7c..50dcaeac9 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 @@ -268,6 +268,17 @@ public boolean equals(Object o) { return type == other.type && clazz.equals(other.clazz); } + /** + * Applies a visitor to this location. + * + * @param the return type of the visitor's methods + * @param

the type of the additional parameter to the visitor's methods + * @param v the visitor operating on this type + * @param p additional parameter to the visitor + * @return a visitor-specified result + */ + abstract R accept(LocationVisitor v, P p); + @Override public int hashCode() { return Objects.hash(type, clazz); diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationVisitor.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationVisitor.java new file mode 100644 index 000000000..83f82332d --- /dev/null +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationVisitor.java @@ -0,0 +1,69 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nima Karimipour + * + * 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.location; + +/** + * A visitor of types, in the style of the visitor design pattern. When a visitor is passed to a + * location's {@link Location#accept accept} method, the visitXyz method + * applicable to that location is invoked. + */ +public interface LocationVisitor { + + /** + * Visits a location for a method. + * + * @param onMethod the location for a method + * @param p a visitor-specified parameter + * @return a visitor-specified result + */ + R visitMethod(OnMethod onMethod, P p); + + /** + * Visits a location for a field. + * + * @param onField the location for a field + * @param p a visitor-specified parameter + * @return a visitor-specified result + */ + R visitField(OnField onField, P p); + + /** + * Visits a location for a parameter. + * + * @param onParameter the location for a parameter + * @param p a visitor-specified parameter + * @return a visitor-specified result + */ + R visitParameter(OnParameter onParameter, P p); + + /** + * Visits a location for a class. + * + * @param onClass the location for a class + * @param p a visitor-specified parameter + * @return a visitor-specified result + */ + R visitClass(OnClass onClass, P p); +} 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 b9270eb39..1ac715c93 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 @@ -81,6 +81,11 @@ protected void fillJsonInformation(JSONObject res) { // no op } + @Override + R accept(LocationVisitor v, P p) { + return v.visitClass(this, p); + } + @Override public String toString() { return "OnClass{" + "type=" + type + ", clazz='" + clazz + '\'' + ", path=" + path + '}'; 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 6381e2a9c..0261f6ab2 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 @@ -129,6 +129,11 @@ public boolean equals(Object o) { return super.equals(other) && !Collections.disjoint(variables, other.variables); } + @Override + R accept(LocationVisitor v, P p) { + return v.visitField(this, p); + } + @Override public int hashCode() { return Objects.hash(super.hashCode(), variables); 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 499322998..85b03a78a 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 @@ -117,6 +117,11 @@ public boolean equals(Object o) { return super.equals(other) && method.equals(other.method); } + @Override + R accept(LocationVisitor v, P p) { + return v.visitMethod(this, p); + } + @Override public int hashCode() { return Objects.hash(super.hashCode(), method); 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 db5763a30..7279c6b0f 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 @@ -125,6 +125,11 @@ public boolean equals(Object o) { return super.equals(other) && method.equals(other.method) && index == other.index; } + @Override + R accept(LocationVisitor v, P p) { + return v.visitParameter(this, p); + } + @Override public int hashCode() { return Objects.hash(super.hashCode(), method, index); From ed7bda6541f69b02f78cb1a32aca241764d37e76 Mon Sep 17 00:00:00 2001 From: nimakarimipour Date: Tue, 4 Apr 2023 11:50:56 -0700 Subject: [PATCH 02/35] update visitibility to public --- .../main/java/edu/ucr/cs/riple/injector/location/Location.java | 2 +- .../main/java/edu/ucr/cs/riple/injector/location/OnClass.java | 2 +- .../main/java/edu/ucr/cs/riple/injector/location/OnField.java | 2 +- .../main/java/edu/ucr/cs/riple/injector/location/OnMethod.java | 2 +- .../java/edu/ucr/cs/riple/injector/location/OnParameter.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) 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 50dcaeac9..f19fd9ed7 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 @@ -277,7 +277,7 @@ public boolean equals(Object o) { * @param p additional parameter to the visitor * @return a visitor-specified result */ - abstract R accept(LocationVisitor v, P p); + public abstract R accept(LocationVisitor v, P p); @Override public int hashCode() { 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 1ac715c93..86af0b459 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 @@ -82,7 +82,7 @@ protected void fillJsonInformation(JSONObject res) { } @Override - R accept(LocationVisitor v, P p) { + public R accept(LocationVisitor v, P p) { return v.visitClass(this, p); } 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 0261f6ab2..e4de051a0 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 @@ -130,7 +130,7 @@ public boolean equals(Object o) { } @Override - R accept(LocationVisitor v, P p) { + public R accept(LocationVisitor v, P p) { return v.visitField(this, p); } 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 85b03a78a..c57287d4b 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 @@ -118,7 +118,7 @@ public boolean equals(Object o) { } @Override - R accept(LocationVisitor v, P p) { + public R accept(LocationVisitor v, P p) { return v.visitMethod(this, p); } 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 7279c6b0f..b1aaad7c1 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 @@ -126,7 +126,7 @@ public boolean equals(Object o) { } @Override - R accept(LocationVisitor v, P p) { + public R accept(LocationVisitor v, P p) { return v.visitParameter(this, p); } From e64c75495524baad8d2b4c4a76effe97f7bbe2e7 Mon Sep 17 00:00:00 2001 From: nimakarimipour Date: Tue, 4 Apr 2023 16:08:13 -0700 Subject: [PATCH 03/35] use visitor for json creation of locations --- .../cs/riple/injector/location/Location.java | 18 ----- .../location/LocationToJsonVisitor.java | 73 +++++++++++++++++++ .../cs/riple/injector/location/OnClass.java | 6 -- .../cs/riple/injector/location/OnField.java | 10 --- .../cs/riple/injector/location/OnMethod.java | 7 -- .../riple/injector/location/OnParameter.java | 8 -- 6 files changed, 73 insertions(+), 49 deletions(-) create mode 100644 injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationToJsonVisitor.java 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 f19fd9ed7..4bf7c643c 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 @@ -38,7 +38,6 @@ import java.util.Objects; import java.util.function.Consumer; import javax.annotation.Nullable; -import org.json.simple.JSONObject; /** Represents a location of an element in the source code. */ public abstract class Location { @@ -126,13 +125,6 @@ public static Location createLocationFromArrayInfo(String[] values) { protected abstract Modification applyToMember( NodeList> members, Change change); - /** - * Fills the given JSON object with the information of this location. - * - * @param res The JSON object to be filled. - */ - protected abstract void fillJsonInformation(JSONObject res); - /** * Applies the change to the target element on the given compilation unit tree. * @@ -152,16 +144,6 @@ public Modification apply(CompilationUnit tree, Change change) { return applyToMember(clazz, change); } - @SuppressWarnings("unchecked") - public JSONObject getJson() { - JSONObject res = new JSONObject(); - res.put(KEYS.CLASS, clazz); - res.put(KEYS.KIND, type.toString()); - res.put(KEYS.PATH, path); - fillJsonInformation(res); - return res; - } - /** * 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/LocationToJsonVisitor.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationToJsonVisitor.java new file mode 100644 index 000000000..f57721ab4 --- /dev/null +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationToJsonVisitor.java @@ -0,0 +1,73 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nima Karimipour + * + * 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.location; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +/** A visitor that converts a location to a JSON object. */ +public class LocationToJsonVisitor implements LocationVisitor { + + @SuppressWarnings("unchecked") + private JSONObject defaultAction(Location location) { + JSONObject res = new JSONObject(); + res.put(Location.KEYS.CLASS, location.clazz); + res.put(Location.KEYS.KIND, location.type.toString()); + res.put(Location.KEYS.PATH, location.path); + return res; + } + + @SuppressWarnings("unchecked") + @Override + public JSONObject visitMethod(OnMethod onMethod, Void unused) { + JSONObject res = defaultAction(onMethod); + res.put(Location.KEYS.METHOD, onMethod.method); + return res; + } + + @SuppressWarnings("unchecked") + @Override + public JSONObject visitField(OnField onField, Void unused) { + JSONObject res = defaultAction(onField); + JSONArray fields = new JSONArray(); + fields.addAll(onField.variables); + res.put(Location.KEYS.VARIABLES, fields); + return res; + } + + @SuppressWarnings("unchecked") + @Override + public JSONObject visitParameter(OnParameter onParameter, Void unused) { + JSONObject res = defaultAction(onParameter); + res.put(Location.KEYS.METHOD, onParameter.method); + res.put(Location.KEYS.INDEX, onParameter.index); + return res; + } + + @Override + public JSONObject visitClass(OnClass onClass, Void unused) { + return defaultAction(onClass); + } +} 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 86af0b459..3260d1d91 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 @@ -34,7 +34,6 @@ import java.util.Optional; import java.util.regex.Pattern; import javax.annotation.Nullable; -import org.json.simple.JSONObject; /** Represents a location for class element. This location is used to apply changes to a class. */ public class OnClass extends Location { @@ -76,11 +75,6 @@ public static boolean isAnonymousClassFlatName(String flatName) { return anonymousClassPattern.matcher(flatName).matches(); } - @Override - protected void fillJsonInformation(JSONObject res) { - // no op - } - @Override public R accept(LocationVisitor v, P p) { return v.visitClass(this, p); 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 e4de051a0..ec4380274 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 @@ -37,8 +37,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import javax.annotation.Nullable; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; /** * Represents a location for field element. This location is used to apply changes to a class field. @@ -68,14 +66,6 @@ public OnField(String path, String clazz, Set variables) { this(Helper.deserializePath(path), clazz, variables); } - @SuppressWarnings("unchecked") - @Override - protected void fillJsonInformation(JSONObject res) { - JSONArray fields = new JSONArray(); - fields.addAll(variables); - res.put(KEYS.VARIABLES, fields); - } - @Override @Nullable protected Modification applyToMember(NodeList> members, Change change) { 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 c57287d4b..1da79b3a5 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 @@ -35,7 +35,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import javax.annotation.Nullable; -import org.json.simple.JSONObject; /** Represents a location for method element. This location is used to apply changes to a method. */ public class OnMethod extends Location { @@ -58,12 +57,6 @@ public OnMethod(String path, String clazz, String method) { this(Helper.deserializePath(path), clazz, method); } - @SuppressWarnings("unchecked") - @Override - protected void fillJsonInformation(JSONObject res) { - res.put(KEYS.METHOD, method); - } - @Override @Nullable protected Modification applyToMember(NodeList> members, Change change) { 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 b1aaad7c1..2c10a32ff 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 @@ -37,7 +37,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import javax.annotation.Nullable; -import org.json.simple.JSONObject; /** * Represents a location for parameter element. This location is used to apply changes to a @@ -66,13 +65,6 @@ public OnParameter(String path, String clazz, String method, int index) { this(Helper.deserializePath(path), clazz, method, index); } - @SuppressWarnings("unchecked") - @Override - protected void fillJsonInformation(JSONObject res) { - res.put(KEYS.METHOD, method); - res.put(KEYS.INDEX, index); - } - @Override @Nullable protected Modification applyToMember(NodeList> members, Change change) { From d695841618a028b377a80bb839ad632664277862 Mon Sep 17 00:00:00 2001 From: nimakarimipour Date: Tue, 4 Apr 2023 16:12:35 -0700 Subject: [PATCH 04/35] add visit method for visitor --- .../java/edu/ucr/cs/riple/injector/changes/Change.java | 3 ++- .../riple/injector/location/LocationToJsonVisitor.java | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) 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 b834625db..4c5065ead 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 @@ -27,6 +27,7 @@ import com.github.javaparser.ast.nodeTypes.NodeWithRange; import edu.ucr.cs.riple.injector.Helper; import edu.ucr.cs.riple.injector.location.Location; +import edu.ucr.cs.riple.injector.location.LocationToJsonVisitor; import edu.ucr.cs.riple.injector.modifications.Modification; import java.util.Objects; import javax.annotation.Nullable; @@ -89,7 +90,7 @@ public int hashCode() { @SuppressWarnings("unchecked") public JSONObject getJson() { JSONObject res = new JSONObject(); - res.put("LOCATION", location.getJson()); + res.put("LOCATION", new LocationToJsonVisitor().visit(location)); res.put("ANNOTATION", annotation); return res; } diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationToJsonVisitor.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationToJsonVisitor.java index f57721ab4..f1f78b05a 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationToJsonVisitor.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationToJsonVisitor.java @@ -70,4 +70,14 @@ public JSONObject visitParameter(OnParameter onParameter, Void unused) { public JSONObject visitClass(OnClass onClass, Void unused) { return defaultAction(onClass); } + + /** + * Visits a location and returns a JSON object. + * + * @param location the location to visit + * @return a JSON object representing the location + */ + public JSONObject visit(Location location) { + return location.accept(this, null); + } } From 8eb661449272acec7196278e02d9eb8482c3c2bd Mon Sep 17 00:00:00 2001 From: nimakarimipour Date: Tue, 4 Apr 2023 16:16:54 -0700 Subject: [PATCH 05/35] make singleton --- .../edu/ucr/cs/riple/injector/changes/Change.java | 2 +- .../injector/location/LocationToJsonVisitor.java | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) 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 4c5065ead..c4c20d35b 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 @@ -90,7 +90,7 @@ public int hashCode() { @SuppressWarnings("unchecked") public JSONObject getJson() { JSONObject res = new JSONObject(); - res.put("LOCATION", new LocationToJsonVisitor().visit(location)); + res.put("LOCATION", LocationToJsonVisitor.INSTANCE.visit(location)); res.put("ANNOTATION", annotation); return res; } diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationToJsonVisitor.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationToJsonVisitor.java index f1f78b05a..917c19ef9 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationToJsonVisitor.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationToJsonVisitor.java @@ -27,9 +27,19 @@ import org.json.simple.JSONArray; import org.json.simple.JSONObject; -/** A visitor that converts a location to a JSON object. */ +/** + * A visitor that converts a location to a JSON object. This visitor is singleton and for all + * methods {@link LocationToJsonVisitor#INSTANCE} should be used. + */ public class LocationToJsonVisitor implements LocationVisitor { + /** A singleton instance of this visitor. */ + public static final LocationToJsonVisitor INSTANCE = new LocationToJsonVisitor(); + + private LocationToJsonVisitor() { + // Singleton instance. + } + @SuppressWarnings("unchecked") private JSONObject defaultAction(Location location) { JSONObject res = new JSONObject(); From 42048675f0d5beeb1b3ec289c0d8c90126c78c8b Mon Sep 17 00:00:00 2001 From: nimakarimipour Date: Tue, 4 Apr 2023 20:13:19 -0700 Subject: [PATCH 06/35] move KEYS to LocationToJsonVisitor --- .../cs/riple/injector/location/Location.java | 10 -------- .../location/LocationToJsonVisitor.java | 24 +++++++++++++------ 2 files changed, 17 insertions(+), 17 deletions(-) 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 4bf7c643c..5bab029fe 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 @@ -49,16 +49,6 @@ public abstract class Location { /** The path to the file containing the element. */ public Path path; - /** The Keys used to represent a location in JSON format */ - public enum KEYS { - VARIABLES, - METHOD, - KIND, - CLASS, - PATH, - INDEX - } - /** * Creates an instance of {@link Location} for a given type, path and class. This constructor is a * base class for all subclasses and must provide these values upon instantiation. diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationToJsonVisitor.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationToJsonVisitor.java index 917c19ef9..3a925cabc 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationToJsonVisitor.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/LocationToJsonVisitor.java @@ -33,6 +33,16 @@ */ public class LocationToJsonVisitor implements LocationVisitor { + /** The Keys used to represent a location in JSON format */ + public enum KEYS { + VARIABLES, + METHOD, + KIND, + CLASS, + PATH, + INDEX + } + /** A singleton instance of this visitor. */ public static final LocationToJsonVisitor INSTANCE = new LocationToJsonVisitor(); @@ -43,9 +53,9 @@ private LocationToJsonVisitor() { @SuppressWarnings("unchecked") private JSONObject defaultAction(Location location) { JSONObject res = new JSONObject(); - res.put(Location.KEYS.CLASS, location.clazz); - res.put(Location.KEYS.KIND, location.type.toString()); - res.put(Location.KEYS.PATH, location.path); + res.put(KEYS.CLASS, location.clazz); + res.put(KEYS.KIND, location.type.toString()); + res.put(KEYS.PATH, location.path); return res; } @@ -53,7 +63,7 @@ private JSONObject defaultAction(Location location) { @Override public JSONObject visitMethod(OnMethod onMethod, Void unused) { JSONObject res = defaultAction(onMethod); - res.put(Location.KEYS.METHOD, onMethod.method); + res.put(KEYS.METHOD, onMethod.method); return res; } @@ -63,7 +73,7 @@ public JSONObject visitField(OnField onField, Void unused) { JSONObject res = defaultAction(onField); JSONArray fields = new JSONArray(); fields.addAll(onField.variables); - res.put(Location.KEYS.VARIABLES, fields); + res.put(KEYS.VARIABLES, fields); return res; } @@ -71,8 +81,8 @@ public JSONObject visitField(OnField onField, Void unused) { @Override public JSONObject visitParameter(OnParameter onParameter, Void unused) { JSONObject res = defaultAction(onParameter); - res.put(Location.KEYS.METHOD, onParameter.method); - res.put(Location.KEYS.INDEX, onParameter.index); + res.put(KEYS.METHOD, onParameter.method); + res.put(KEYS.INDEX, onParameter.index); return res; } From 7d0a2ce9645328a99c6e7ab8546eca3959abfcdd Mon Sep 17 00:00:00 2001 From: nimakarimipour Date: Fri, 21 Apr 2023 01:09:55 -0700 Subject: [PATCH 07/35] use stack instead --- .../src/main/java/edu/ucr/cs/riple/injector/Helper.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java b/injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java index f3617aacb..affe13818 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java @@ -41,12 +41,12 @@ import java.io.FileNotFoundException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Optional; -import java.util.Stack; import java.util.function.Predicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -491,7 +491,7 @@ public static boolean srcIsUnderClassClassPath(Path path, String rootPackage) { * any {@link BodyDeclaration}. */ public static final class DirectMethodParentIterator implements Iterator { - private final Stack stack = new Stack<>(); + private final ArrayDeque stack = new ArrayDeque<>(); public DirectMethodParentIterator(CallableDeclaration node) { stack.add(node); @@ -504,7 +504,7 @@ public boolean hasNext() { @Override public Node next() { - Node next = stack.pop(); + Node next = stack.removeFirst(); List children = next.getChildNodes(); for (int i = children.size() - 1; i >= 0; i--) { Node child = children.get(i); From e1223441a6613c67f8a4b5d170dbba1841c22a04 Mon Sep 17 00:00:00 2001 From: nimakarimipour Date: Fri, 21 Apr 2023 01:15:03 -0700 Subject: [PATCH 08/35] add comment --- .../main/java/edu/ucr/cs/riple/injector/Helper.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java b/injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java index affe13818..5dc9df25e 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java @@ -340,6 +340,9 @@ public static NodeList> getTypeDeclarationMembersByFlatName( @Nullable public static VariableDeclarationExpr locateVariableDeclarationExpr( CallableDeclaration encMethod, String varName) { + // Should not visit inner nodes of inner methods in the given method, since the given + // method should be the closest enclosing method of the target local variable. Therefore, we + // use DirectMethodParentIterator to skip inner methods. Iterator treeIterator = new DirectMethodParentIterator(encMethod); while (treeIterator.hasNext()) { Node n = treeIterator.next(); @@ -491,26 +494,26 @@ public static boolean srcIsUnderClassClassPath(Path path, String rootPackage) { * any {@link BodyDeclaration}. */ public static final class DirectMethodParentIterator implements Iterator { - private final ArrayDeque stack = new ArrayDeque<>(); + private final ArrayDeque deque = new ArrayDeque<>(); public DirectMethodParentIterator(CallableDeclaration node) { - stack.add(node); + deque.add(node); } @Override public boolean hasNext() { - return !stack.isEmpty(); + return !deque.isEmpty(); } @Override public Node next() { - Node next = stack.removeFirst(); + Node next = deque.removeFirst(); List children = next.getChildNodes(); for (int i = children.size() - 1; i >= 0; i--) { Node child = children.get(i); if (!(child instanceof BodyDeclaration)) { // Skip over any CallableDeclaration. - stack.add(children.get(i)); + deque.add(children.get(i)); } } return next; From 6d3f3e49549ec61f5610019df8cd248d025a66e4 Mon Sep 17 00:00:00 2001 From: nimakarimipour Date: Fri, 21 Apr 2023 01:15:40 -0700 Subject: [PATCH 09/35] add a new line for field --- injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java b/injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java index 5dc9df25e..14cabca87 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java @@ -494,6 +494,7 @@ public static boolean srcIsUnderClassClassPath(Path path, String rootPackage) { * any {@link BodyDeclaration}. */ public static final class DirectMethodParentIterator implements Iterator { + private final ArrayDeque deque = new ArrayDeque<>(); public DirectMethodParentIterator(CallableDeclaration node) { From fb7e8a72819decab5175ab6da6f532ac4222e597 Mon Sep 17 00:00:00 2001 From: nimakarimipour Date: Fri, 21 Apr 2023 01:18:39 -0700 Subject: [PATCH 10/35] use OnMethod instance for local variable location: --- .../riple/injector/changes/ChangeVisitor.java | 2 +- .../injector/location/OnLocalVariable.java | 24 +++---------------- 2 files changed, 4 insertions(+), 22 deletions(-) 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 index 9b708e1a5..8f8aba289 100644 --- 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 @@ -183,7 +183,7 @@ public Modification visitLocalVariable( // already found the member. return; } - if (onLocalVariable.matchesCallableDeclaration(callableDeclaration)) { + if (onLocalVariable.encMethod.matchesCallableDeclaration(callableDeclaration)) { // Find variable declaration in the callable declaration with the variable name. VariableDeclarationExpr variableDeclarationExpr = Helper.locateVariableDeclarationExpr( diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnLocalVariable.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnLocalVariable.java index cc21a5d6a..292b6c373 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnLocalVariable.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnLocalVariable.java @@ -24,9 +24,7 @@ package edu.ucr.cs.riple.injector.location; -import com.github.javaparser.ast.body.CallableDeclaration; import edu.ucr.cs.riple.injector.Helper; -import edu.ucr.cs.riple.injector.SignatureMatcher; import java.nio.file.Path; import java.util.Objects; import java.util.function.Consumer; @@ -42,20 +40,14 @@ */ public class OnLocalVariable extends Location { - /** Enclosing method signature of the target local variable. */ - public final String encMethod; - /** - * Matcher for the method signature. Method signature is given as a string, this matcher is used - * to match the target. - */ - public final SignatureMatcher matcher; + /** Enclosing method of the target local variable. */ + public final OnMethod encMethod; /** Name of the local variable. */ public final String varName; public OnLocalVariable(Path path, String clazz, String encMethod, String varName) { super(LocationKind.LOCAL_VARIABLE, path, clazz); - this.encMethod = encMethod; - this.matcher = new SignatureMatcher(encMethod); + this.encMethod = new OnMethod(path, clazz, encMethod); this.varName = varName; } @@ -106,14 +98,4 @@ public String toString() { + varName + '}'; } - - /** - * 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); - } } From 1e8b4023e32d218a040e67489533bd2a228bbd62 Mon Sep 17 00:00:00 2001 From: nimakarimipour Date: Fri, 21 Apr 2023 01:26:06 -0700 Subject: [PATCH 11/35] add javadoc --- .../riple/injector/modifications/TypeChangeVisitor.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/TypeChangeVisitor.java b/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/TypeChangeVisitor.java index 2fcadb0f7..09488a5af 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/TypeChangeVisitor.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/TypeChangeVisitor.java @@ -30,7 +30,13 @@ import java.util.HashSet; import java.util.Set; -/** Visitor for making changes on the type and all of its type arguments. */ +/** + * Visitor for computing the required set of {@link Modification} instances for the given {@link + * Change} request on the type and all of its type arguments. This visitor will assume that the + * requested change, should be applied to the type and all containing type parameters if exists. For + * instance, an annotation addition on type {@code Foo>} will be treated as the + * output should be {@code @Annot Foo<@Annot Bar, @Annot Map<@Annot Bar, @Annot Bar>>}. + */ public class TypeChangeVisitor extends GenericVisitorWithDefaults, Change> { @Override From cc8695984d7c19088d9b13bd2f5051dff5004c98 Mon Sep 17 00:00:00 2001 From: nimakarimipour Date: Mon, 1 May 2023 17:09:05 -0700 Subject: [PATCH 12/35] update --- .../modifications/TypeChangeVisitor.java | 11 +++++++ .../OnLocalVariableInjectionTest.java | 33 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/TypeChangeVisitor.java b/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/TypeChangeVisitor.java index 09488a5af..3f1469307 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/TypeChangeVisitor.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/TypeChangeVisitor.java @@ -22,6 +22,7 @@ import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.type.ArrayType; import com.github.javaparser.ast.type.ClassOrInterfaceType; import com.github.javaparser.ast.type.PrimitiveType; import com.github.javaparser.ast.type.WildcardType; @@ -80,6 +81,16 @@ public Set visit(WildcardType type, Change change) { return result; } + @Override + public Set visit(ArrayType type, Change change) { + Set result = new HashSet<>(type.getComponentType().accept(this, change)); + Modification modification = change.visit(type); + if (modification != null) { + result.add(modification); + } + return result; + } + @Override public Set defaultAction(NodeList n, Change arg) { throw new RuntimeException("Unexpected type in TypeChangeVisitor: " + n.getClass().getName()); diff --git a/injector/src/test/java/edu/ucr/cs/riple/injector/OnLocalVariableInjectionTest.java b/injector/src/test/java/edu/ucr/cs/riple/injector/OnLocalVariableInjectionTest.java index 3d8fc5735..e88b5e238 100644 --- a/injector/src/test/java/edu/ucr/cs/riple/injector/OnLocalVariableInjectionTest.java +++ b/injector/src/test/java/edu/ucr/cs/riple/injector/OnLocalVariableInjectionTest.java @@ -168,4 +168,37 @@ public void onLocalVariableWithIdenticalExistingInnerMethodLocalVariable() { new OnLocalVariable("Foo.java", "test.Foo", "foo()", "f0"), "edu.ucr.UnTainted")) .start(); } + + @Test + public void onArrayType() { + injectorTestHelper + .addInput( + "Foo.java", + "package test;", + "public class Foo {", + " public void foo() {", + " Map[] f0;", + " T[] f1;", + " String[] f2;", + " }", + "}") + .expectOutput( + "package test;", + "import edu.ucr.UnTainted;", + "public class Foo {", + " public void foo() {", + " @UnTainted Map<@UnTainted String, @UnTainted T[]>[] f0;", + " @UnTainted T[] f1;", + " @UnTainted String[] f2;", + " }", + "}") + .addChanges( + new AddMarkerAnnotation( + new OnLocalVariable("Foo.java", "test.Foo", "foo()", "f0"), "edu.ucr.UnTainted"), + new AddMarkerAnnotation( + new OnLocalVariable("Foo.java", "test.Foo", "foo()", "f1"), "edu.ucr.UnTainted"), + new AddMarkerAnnotation( + new OnLocalVariable("Foo.java", "test.Foo", "foo()", "f2"), "edu.ucr.UnTainted")) + .start(); + } } From a3c4b1d5023e7a09582144f1cfe30abad021b7a5 Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Tue, 9 May 2023 10:15:32 -0700 Subject: [PATCH 13/35] update --- .../riple/injector/changes/ChangeVisitor.java | 14 +-- .../MultiPositionModification.java | 62 ----------- .../modifications/TypeChangeVisitor.java | 103 ------------------ .../OnLocalVariableInjectionTest.java | 6 +- 4 files changed, 4 insertions(+), 181 deletions(-) delete mode 100644 injector/src/main/java/edu/ucr/cs/riple/injector/modifications/MultiPositionModification.java delete mode 100644 injector/src/main/java/edu/ucr/cs/riple/injector/modifications/TypeChangeVisitor.java 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 index 8f8aba289..f4f813ef9 100644 --- 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 @@ -41,11 +41,7 @@ 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 edu.ucr.cs.riple.injector.modifications.MultiPositionModification; -import edu.ucr.cs.riple.injector.modifications.TypeChangeVisitor; -import java.util.HashSet; import java.util.Optional; -import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Nullable; @@ -200,15 +196,7 @@ public Modification visitLocalVariable( .toString() .equals(onLocalVariable.varName)) { // Located the variable. - Set modifications = new HashSet<>(); - // Process the declaration statement. - modifications.add(change.visit(variableDeclarationExpr)); - // Process the declarator type arguments. - modifications.addAll( - variableDeclarator - .getType() - .accept(new TypeChangeVisitor(), change)); - ans.set(new MultiPositionModification(modifications)); + ans.set(change.visit(variableDeclarationExpr)); } }); } diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/MultiPositionModification.java b/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/MultiPositionModification.java deleted file mode 100644 index 3e16e0f96..000000000 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/MultiPositionModification.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2023 Nima Karimipour - * - * 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.modifications; - -import com.github.javaparser.Position; -import edu.ucr.cs.riple.injector.offsets.FileOffsetStore; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; - -/** Represents a composite modification that consists of multiple modifications. */ -public class MultiPositionModification implements Modification { - - /** The modifications that should be applied to the source file in the reverse order. */ - final SortedSet modifications; - - public MultiPositionModification(Set modifications) { - // Modification are sorted to start from the last position, to make the changes ineffective to - // current computed offsets. - this.modifications = new TreeSet<>(Collections.reverseOrder()); - this.modifications.addAll(modifications); - } - - @Override - public void visit(List lines, FileOffsetStore offsetStore) { - this.modifications.forEach(modification -> modification.visit(lines, offsetStore)); - } - - @Override - public Position getStartingPosition() { - return modifications.last().getStartingPosition(); - } - - @Override - public int compareTo(Modification o) { - return COMPARATOR.compare(this, o); - } -} diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/TypeChangeVisitor.java b/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/TypeChangeVisitor.java deleted file mode 100644 index 3f1469307..000000000 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/TypeChangeVisitor.java +++ /dev/null @@ -1,103 +0,0 @@ -/** - * MIT License - * - *

Copyright (c) 2023 Nima Karimipour - * - *

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.modifications; - -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.NodeList; -import com.github.javaparser.ast.type.ArrayType; -import com.github.javaparser.ast.type.ClassOrInterfaceType; -import com.github.javaparser.ast.type.PrimitiveType; -import com.github.javaparser.ast.type.WildcardType; -import com.github.javaparser.ast.visitor.GenericVisitorWithDefaults; -import edu.ucr.cs.riple.injector.changes.Change; -import java.util.HashSet; -import java.util.Set; - -/** - * Visitor for computing the required set of {@link Modification} instances for the given {@link - * Change} request on the type and all of its type arguments. This visitor will assume that the - * requested change, should be applied to the type and all containing type parameters if exists. For - * instance, an annotation addition on type {@code Foo>} will be treated as the - * output should be {@code @Annot Foo<@Annot Bar, @Annot Map<@Annot Bar, @Annot Bar>>}. - */ -public class TypeChangeVisitor extends GenericVisitorWithDefaults, Change> { - - @Override - public Set visit(PrimitiveType n, Change change) { - if (n.getRange().isEmpty()) { - return Set.of(); - } - Modification modification = change.visit(n); - return modification == null ? Set.of() : Set.of(modification); - } - - @Override - public Set visit(ClassOrInterfaceType classOrInterfaceType, Change change) { - Set result = new HashSet<>(); - Modification modification = change.visit(classOrInterfaceType); - if (modification != null) { - result.add(modification); - } - if (classOrInterfaceType.getTypeArguments().isPresent()) { - classOrInterfaceType - .getTypeArguments() - .get() - .forEach(e -> result.addAll(e.accept(this, change))); - } - return result; - } - - @Override - public Set visit(WildcardType type, Change change) { - Set result = new HashSet<>(); - Modification modification = change.visit(type); - if (modification != null) { - result.add(modification); - } - if (type.getExtendedType().isPresent()) { - result.addAll(type.getExtendedType().get().accept(this, change)); - } - if (type.getSuperType().isPresent()) { - result.addAll(type.getSuperType().get().accept(this, change)); - } - return result; - } - - @Override - public Set visit(ArrayType type, Change change) { - Set result = new HashSet<>(type.getComponentType().accept(this, change)); - Modification modification = change.visit(type); - if (modification != null) { - result.add(modification); - } - return result; - } - - @Override - public Set defaultAction(NodeList n, Change arg) { - throw new RuntimeException("Unexpected type in TypeChangeVisitor: " + n.getClass().getName()); - } - - @Override - public Set defaultAction(Node n, Change arg) { - throw new RuntimeException("Unexpected type in TypeChangeVisitor: " + n.getClass().getName()); - } -} diff --git a/injector/src/test/java/edu/ucr/cs/riple/injector/OnLocalVariableInjectionTest.java b/injector/src/test/java/edu/ucr/cs/riple/injector/OnLocalVariableInjectionTest.java index e88b5e238..ab7c4727b 100644 --- a/injector/src/test/java/edu/ucr/cs/riple/injector/OnLocalVariableInjectionTest.java +++ b/injector/src/test/java/edu/ucr/cs/riple/injector/OnLocalVariableInjectionTest.java @@ -53,7 +53,7 @@ public void additionTest() { "public class Foo {", " public void foo() {", " @UnTainted int f0;", - " @UnTainted Bar<@UnTainted String, @UnTainted Integer, @UnTainted Baz<@UnTainted String, @UnTainted Integer>> f1;", + " @UnTainted Bar> f1;", " @UnTainted String f2;", " }", "}") @@ -77,7 +77,7 @@ public void deletionTest() { "public class Foo {", " public void foo() {", " @UnTainted int f0;", - " @UnTainted Bar<@UnTainted String, @UnTainted Integer, @UnTainted Baz<@UnTainted String, @UnTainted Integer>> f1;", + " @UnTainted Bar> f1;", " @UnTainted String f2;", " }", "}") @@ -187,7 +187,7 @@ public void onArrayType() { "import edu.ucr.UnTainted;", "public class Foo {", " public void foo() {", - " @UnTainted Map<@UnTainted String, @UnTainted T[]>[] f0;", + " @UnTainted Map[] f0;", " @UnTainted T[] f1;", " @UnTainted String[] f2;", " }", From 0e8483cf8280e618183126ef14646cb260da5022 Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Tue, 9 May 2023 15:54:59 -0700 Subject: [PATCH 14/35] update --- .../edu/ucr/cs/riple/injector/changes/ChangeVisitor.java | 6 +++--- .../cs/riple/injector/OnLocalVariableInjectionTest.java | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) 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 index b9a3ef3b7..2c739cc33 100644 --- 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 @@ -169,9 +169,9 @@ public Modification visitClass( @Override public Modification visitLocalVariable( - OnLocalVariable onLocalVariable, Pair>, Change> pair) { + OnLocalVariable onLocalVariable, Pair>, ASTChange> pair) { final NodeList> members = pair.a; - final Change change = pair.b; + final ASTChange change = pair.b; final AtomicReference ans = new AtomicReference<>(); members.forEach( bodyDeclaration -> @@ -198,7 +198,7 @@ public Modification visitLocalVariable( .toString() .equals(onLocalVariable.varName)) { // Located the variable. - ans.set(change.visit(variableDeclarationExpr)); + ans.set(change.computeTextModificationOn(variableDeclarationExpr)); } }); } diff --git a/injector/src/test/java/edu/ucr/cs/riple/injector/OnLocalVariableInjectionTest.java b/injector/src/test/java/edu/ucr/cs/riple/injector/OnLocalVariableInjectionTest.java index ab7c4727b..2a1d22a2c 100644 --- a/injector/src/test/java/edu/ucr/cs/riple/injector/OnLocalVariableInjectionTest.java +++ b/injector/src/test/java/edu/ucr/cs/riple/injector/OnLocalVariableInjectionTest.java @@ -25,7 +25,7 @@ package edu.ucr.cs.riple.injector; import edu.ucr.cs.riple.injector.changes.AddMarkerAnnotation; -import edu.ucr.cs.riple.injector.changes.RemoveAnnotation; +import edu.ucr.cs.riple.injector.changes.RemoveMarkerAnnotation; import edu.ucr.cs.riple.injector.location.OnLocalVariable; import org.junit.Test; import org.junit.runner.RunWith; @@ -92,11 +92,11 @@ public void deletionTest() { " }", "}") .addChanges( - new RemoveAnnotation( + new RemoveMarkerAnnotation( new OnLocalVariable("Foo.java", "test.Foo", "foo()", "f0"), "edu.ucr.UnTainted"), - new RemoveAnnotation( + new RemoveMarkerAnnotation( new OnLocalVariable("Foo.java", "test.Foo", "foo()", "f1"), "edu.ucr.UnTainted"), - new RemoveAnnotation( + new RemoveMarkerAnnotation( new OnLocalVariable("Foo.java", "test.Foo", "foo()", "f2"), "edu.ucr.UnTainted")) .start(); } From 65034033df1e423a0e7232c2ea9388f7e4d43a5a Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Tue, 9 May 2023 15:55:37 -0700 Subject: [PATCH 15/35] intial impl --- .../changes/AddTypeUseMarkerAnnotation.java | 32 +++++++ .../RemoveTypeUseMarkerAnnotation.java | 95 +++++++++++++++++++ .../MultiPositionModification.java | 62 ++++++++++++ .../riple/injector/TypeUseAnnotationTest.java | 28 ++++++ 4 files changed, 217 insertions(+) create mode 100644 injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddTypeUseMarkerAnnotation.java create mode 100644 injector/src/main/java/edu/ucr/cs/riple/injector/changes/RemoveTypeUseMarkerAnnotation.java create mode 100644 injector/src/main/java/edu/ucr/cs/riple/injector/modifications/MultiPositionModification.java create mode 100644 injector/src/test/java/edu/ucr/cs/riple/injector/TypeUseAnnotationTest.java diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddTypeUseMarkerAnnotation.java b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddTypeUseMarkerAnnotation.java new file mode 100644 index 000000000..aeb58f9bb --- /dev/null +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddTypeUseMarkerAnnotation.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 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 edu.ucr.cs.riple.injector.location.Location; + +public class AddTypeUseMarkerAnnotation extends AddMarkerAnnotation{ + + public AddTypeUseMarkerAnnotation(Location location, String annotation) { + super(location, annotation); + } +} \ No newline at end of file diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/RemoveTypeUseMarkerAnnotation.java b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/RemoveTypeUseMarkerAnnotation.java new file mode 100644 index 000000000..42926e119 --- /dev/null +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/RemoveTypeUseMarkerAnnotation.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2022 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 com.github.javaparser.Range; +import com.github.javaparser.ast.expr.AnnotationExpr; +import com.github.javaparser.ast.expr.MarkerAnnotationExpr; +import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations; +import com.github.javaparser.ast.nodeTypes.NodeWithRange; +import com.github.javaparser.ast.type.ClassOrInterfaceType; +import com.github.javaparser.ast.type.Type; +import edu.ucr.cs.riple.injector.Helper; +import edu.ucr.cs.riple.injector.location.Location; +import edu.ucr.cs.riple.injector.modifications.Deletion; +import edu.ucr.cs.riple.injector.modifications.Modification; +import edu.ucr.cs.riple.injector.modifications.MultiPositionModification; + +import javax.annotation.Nullable; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +public class RemoveTypeUseMarkerAnnotation extends RemoveMarkerAnnotation{ + + public RemoveTypeUseMarkerAnnotation(Location location, String annotation) { + super(location, annotation); + } + + @Override + @Nullable + public & NodeWithRange> Modification computeTextModificationOn( + T node) { + Type type = Helper.getType(node); + AnnotationExpr annotationExpr = new MarkerAnnotationExpr(annotationName.simpleName); + Set modifications = new HashSet<>(); + // Remove annotation the declaration if exists e.g. @Annot String + for (AnnotationExpr expr : node.getAnnotations()) { + if (expr.equals(annotationExpr)) { + Optional annotRange = expr.getRange(); + annotRange + .map(value -> new Deletion(expr.toString(), value.begin, value.end)) + .ifPresent(modifications::add); + } + } + + // Remove annotation the type if exists e.g. java.lang.@Annot String + for (AnnotationExpr expr : type.getAnnotations()) { + if (expr.equals(annotationExpr)) { + Optional annotRange = expr.getRange(); + annotRange + .map(value -> new Deletion(expr.toString(), value.begin, value.end)) + .ifPresent(modifications::add); + } + } + + // Remove annotation from type arguments if exists e.g. List<@Annot String> + if (type instanceof ClassOrInterfaceType) { + ClassOrInterfaceType classOrInterfaceType = (ClassOrInterfaceType) type; + if (classOrInterfaceType.getTypeArguments().isPresent()) { + classOrInterfaceType + .getTypeArguments() + .get() + .forEach( + typeArg -> { + Modification onType = + computeTextModificationOn((NodeWithAnnotations & NodeWithRange) typeArg); + if (onType != null) { + modifications.add(onType); + } + }); + } + } + return modifications.isEmpty() ? null : new MultiPositionModification(modifications); + } +} diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/MultiPositionModification.java b/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/MultiPositionModification.java new file mode 100644 index 000000000..e429afe6c --- /dev/null +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/MultiPositionModification.java @@ -0,0 +1,62 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nima Karimipour + * + * 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.modifications; + +import com.github.javaparser.Position; +import edu.ucr.cs.riple.injector.offsets.FileOffsetStore; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +/** Represents a composite modification that consists of multiple modifications. */ +public class MultiPositionModification implements Modification { + + /** The modifications that should be applied to the source file in the reverse order. */ + final SortedSet modifications; + + public MultiPositionModification(Set modifications) { + // Modification are sorted to start from the last position, to make the changes ineffective to + // current computed offsets. + this.modifications = new TreeSet<>(Collections.reverseOrder()); + this.modifications.addAll(modifications); + } + + @Override + public void visit(List lines, FileOffsetStore offsetStore) { + this.modifications.forEach(modification -> modification.visit(lines, offsetStore)); + } + + @Override + public Position getStartingPosition() { + return modifications.last().getStartingPosition(); + } + + @Override + public int compareTo(Modification o) { + return COMPARATOR.compare(this, o); + } +} \ No newline at end of file diff --git a/injector/src/test/java/edu/ucr/cs/riple/injector/TypeUseAnnotationTest.java b/injector/src/test/java/edu/ucr/cs/riple/injector/TypeUseAnnotationTest.java new file mode 100644 index 000000000..a717a7998 --- /dev/null +++ b/injector/src/test/java/edu/ucr/cs/riple/injector/TypeUseAnnotationTest.java @@ -0,0 +1,28 @@ +/* + * MIT License + * + * Copyright (c) 2022 Nima Karimipour + * + * 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; + +public class TypeUseAnnotationTest { +} From 2f4ac1e81d9beb00d5e88c92f921aaaac85556bf Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Tue, 9 May 2023 16:32:49 -0700 Subject: [PATCH 16/35] update --- .../edu/ucr/cs/riple/injector/Helper.java | 60 +++++ .../injector/changes/AddMarkerAnnotation.java | 18 ++ .../changes/AddSingleElementAnnotation.java | 19 ++ .../changes/AddTypeUseMarkerAnnotation.java | 103 +++++++- .../changes/RemoveMarkerAnnotation.java | 19 ++ .../RemoveTypeUseMarkerAnnotation.java | 60 +++-- .../MultiPositionModification.java | 50 ++-- .../riple/injector/TypeUseAnnotationTest.java | 220 +++++++++++++++++- 8 files changed, 488 insertions(+), 61 deletions(-) diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java b/injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java index 14cabca87..9ba943584 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/Helper.java @@ -33,9 +33,17 @@ import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.EnumConstantDeclaration; import com.github.javaparser.ast.body.EnumDeclaration; +import com.github.javaparser.ast.body.FieldDeclaration; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.body.TypeDeclaration; +import com.github.javaparser.ast.body.VariableDeclarator; +import com.github.javaparser.ast.expr.AnnotationExpr; import com.github.javaparser.ast.expr.ObjectCreationExpr; import com.github.javaparser.ast.expr.VariableDeclarationExpr; +import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations; +import com.github.javaparser.ast.type.ArrayType; +import com.github.javaparser.ast.type.Type; import com.google.common.base.Preconditions; import edu.ucr.cs.riple.injector.exceptions.TargetClassNotFound; import java.io.FileNotFoundException; @@ -357,6 +365,58 @@ public static VariableDeclarationExpr locateVariableDeclarationExpr( return null; } + /** + * Extracts the type of the given node implementing {@link NodeWithAnnotations}. + * + * @param node the node. + * @return the type of the node. + */ + public static Type getType(NodeWithAnnotations node) { + if (node instanceof MethodDeclaration) { + return ((MethodDeclaration) node).getType(); + } + if (node instanceof FieldDeclaration) { + return ((FieldDeclaration) node).getElementType(); + } + if (node instanceof VariableDeclarationExpr) { + NodeList decls = ((VariableDeclarationExpr) node).getVariables(); + for (VariableDeclarator v : decls) { + // All declared variables in a VariableDeclarationExpr have the same type. + // (e.g. Foo a, b, c;) + return v.getType(); + } + } + if (node instanceof Parameter) { + return ((Parameter) node).getType(); + } + if (node instanceof VariableDeclarator) { + return ((VariableDeclarator) node).getType(); + } + if (node instanceof ArrayType) { + return ((ArrayType) node).getComponentType(); + } + if (node instanceof Type) { + return ((Type) node); + } + throw new RuntimeException("Unknown node type: " + node.getClass()); + } + + /** + * Helper method to check if a node is annotated with a specific annotation. + * + * @param node the node to check its annotations. + * @param expr the annotation to check. + * @return true if the node is annotated with the annotation. + */ + public static boolean isAnnotatedWith(Node node, AnnotationExpr expr) { + if (!(node instanceof NodeWithAnnotations)) { + // Node cannot have annotations. + return false; + } + return ((NodeWithAnnotations) node) + .getAnnotations().stream().anyMatch(annot -> annot.equals(expr)); + } + /** * Extracts the integer at the start of string (e.g. 129uid -> 129). * diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddMarkerAnnotation.java b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddMarkerAnnotation.java index 9af8c220f..cfc8ca8fd 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddMarkerAnnotation.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddMarkerAnnotation.java @@ -31,6 +31,7 @@ import edu.ucr.cs.riple.injector.location.Location; import edu.ucr.cs.riple.injector.modifications.Insertion; import edu.ucr.cs.riple.injector.modifications.Modification; +import java.util.Objects; import javax.annotation.Nullable; /** @@ -68,4 +69,21 @@ Modification computeTextModificationOn(T node) { public RemoveAnnotation getReverse() { return new RemoveMarkerAnnotation(location, annotationName.fullName); } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof AddMarkerAnnotation)) { + return false; + } + AddMarkerAnnotation that = (AddMarkerAnnotation) o; + return location.equals(that.location) && annotationName.equals(that.annotationName); + } + + @Override + public int hashCode() { + return Objects.hash("ADD", location, annotationName); + } } diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddSingleElementAnnotation.java b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddSingleElementAnnotation.java index 38515b343..4400f6405 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddSingleElementAnnotation.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddSingleElementAnnotation.java @@ -36,6 +36,7 @@ import edu.ucr.cs.riple.injector.modifications.Insertion; import edu.ucr.cs.riple.injector.modifications.Modification; import edu.ucr.cs.riple.injector.modifications.Replacement; +import java.util.Objects; import java.util.Optional; import javax.annotation.Nullable; @@ -163,4 +164,22 @@ public RemoveAnnotation getReverse() { throw new UnsupportedOperationException( "Annotation deletion for single element annotations is not supported yet."); } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof AddSingleElementAnnotation)) { + return false; + } + if (!super.equals(o)) return false; + AddSingleElementAnnotation that = (AddSingleElementAnnotation) o; + return repeatable == that.repeatable && Objects.equals(argument, that.argument); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), argument, repeatable); + } } diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddTypeUseMarkerAnnotation.java b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddTypeUseMarkerAnnotation.java index aeb58f9bb..23ac29c74 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddTypeUseMarkerAnnotation.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddTypeUseMarkerAnnotation.java @@ -22,11 +22,110 @@ package edu.ucr.cs.riple.injector.changes; +import com.github.javaparser.Range; +import com.github.javaparser.ast.expr.AnnotationExpr; +import com.github.javaparser.ast.expr.MarkerAnnotationExpr; +import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations; +import com.github.javaparser.ast.nodeTypes.NodeWithRange; +import com.github.javaparser.ast.type.ArrayType; +import com.github.javaparser.ast.type.ClassOrInterfaceType; +import com.github.javaparser.ast.type.PrimitiveType; +import com.github.javaparser.ast.type.Type; +import edu.ucr.cs.riple.injector.Helper; import edu.ucr.cs.riple.injector.location.Location; +import edu.ucr.cs.riple.injector.modifications.Insertion; +import edu.ucr.cs.riple.injector.modifications.Modification; +import edu.ucr.cs.riple.injector.modifications.MultiPositionModification; +import java.util.HashSet; +import java.util.Set; +import javax.annotation.Nullable; -public class AddTypeUseMarkerAnnotation extends AddMarkerAnnotation{ +/** Used to add type-use marker annotations on elements in source code. */ +public class AddTypeUseMarkerAnnotation extends AddMarkerAnnotation { public AddTypeUseMarkerAnnotation(Location location, String annotation) { super(location, annotation); } -} \ No newline at end of file + + @Override + @Nullable + public & NodeWithRange> + Modification computeTextModificationOn(T node) { + if (node.getRange().isEmpty()) { + return null; + } + + AnnotationExpr annotationExpr = new MarkerAnnotationExpr(annotationName.simpleName); + + Set modifications = new HashSet<>(); + Type type = Helper.getType(node); + + if (!Helper.isAnnotatedWith(type, annotationExpr)) { + // Annotate the type. + Range range = findTypeRange(type); + if (range == null) { + return null; + } + modifications.add(new Insertion(annotationExpr.toString(), range.begin)); + } + // Annotate type arguments. Note: We currently do not annotate Map[] as @Annot Map<@Annot + // K, @Annot V>[] and will leave it as @Annot Map[] + if (type instanceof ClassOrInterfaceType) { + ClassOrInterfaceType classOrInterfaceType = (ClassOrInterfaceType) type; + if (classOrInterfaceType.getTypeArguments().isPresent()) { + classOrInterfaceType + .getTypeArguments() + .get() + .forEach( + typeArg -> { + Modification onType = + computeTextModificationOn( + (NodeWithAnnotations & NodeWithRange) typeArg); + if (onType != null) { + modifications.add(onType); + } + }); + } + } + return modifications.isEmpty() ? null : new MultiPositionModification(modifications); + } + + @Override + public RemoveAnnotation getReverse() { + return new RemoveTypeUseMarkerAnnotation(location, annotationName.fullName); + } + + private static Range findTypeRange(Type type) { + if (type instanceof ClassOrInterfaceType) { + ClassOrInterfaceType classOrInterfaceType = (ClassOrInterfaceType) type; + if (classOrInterfaceType.getName().getRange().isEmpty()) { + return null; + } + return ((ClassOrInterfaceType) type).getName().getRange().get(); + } + if (type instanceof ArrayType) { + return findTypeRange(((ArrayType) type).getComponentType()); + } + if (type instanceof PrimitiveType) { + if (type.getRange().isEmpty()) { + return null; + } + return type.getRange().get(); + } + throw new RuntimeException( + "Unexpected type to get range from: " + type + " : " + type.getClass()); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof AddTypeUseMarkerAnnotation)) { + return false; + } + AddTypeUseMarkerAnnotation otherAdd = (AddTypeUseMarkerAnnotation) other; + return this.location.equals(otherAdd.location) + && this.annotationName.equals(otherAdd.annotationName); + } +} diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/RemoveMarkerAnnotation.java b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/RemoveMarkerAnnotation.java index 88b018475..b8d3ea72c 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/RemoveMarkerAnnotation.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/RemoveMarkerAnnotation.java @@ -30,6 +30,7 @@ import edu.ucr.cs.riple.injector.location.Location; import edu.ucr.cs.riple.injector.modifications.Deletion; import edu.ucr.cs.riple.injector.modifications.Modification; +import java.util.Objects; import java.util.Optional; import javax.annotation.Nullable; @@ -62,4 +63,22 @@ Modification computeTextModificationOn(T node) { } return null; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof RemoveMarkerAnnotation)) { + return false; + } + RemoveMarkerAnnotation other = (RemoveMarkerAnnotation) o; + return Objects.equals(location, other.location) + && Objects.equals(annotationName, other.annotationName); + } + + @Override + public int hashCode() { + return Objects.hash("REMOVE-MARKER", location, annotationName); + } } diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/RemoveTypeUseMarkerAnnotation.java b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/RemoveTypeUseMarkerAnnotation.java index 42926e119..eb28a1a42 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/RemoveTypeUseMarkerAnnotation.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/RemoveTypeUseMarkerAnnotation.java @@ -34,13 +34,12 @@ import edu.ucr.cs.riple.injector.modifications.Deletion; import edu.ucr.cs.riple.injector.modifications.Modification; import edu.ucr.cs.riple.injector.modifications.MultiPositionModification; - -import javax.annotation.Nullable; import java.util.HashSet; import java.util.Optional; import java.util.Set; +import javax.annotation.Nullable; -public class RemoveTypeUseMarkerAnnotation extends RemoveMarkerAnnotation{ +public class RemoveTypeUseMarkerAnnotation extends RemoveMarkerAnnotation { public RemoveTypeUseMarkerAnnotation(Location location, String annotation) { super(location, annotation); @@ -48,28 +47,24 @@ public RemoveTypeUseMarkerAnnotation(Location location, String annotation) { @Override @Nullable - public & NodeWithRange> Modification computeTextModificationOn( - T node) { + public & NodeWithRange> + Modification computeTextModificationOn(T node) { Type type = Helper.getType(node); AnnotationExpr annotationExpr = new MarkerAnnotationExpr(annotationName.simpleName); Set modifications = new HashSet<>(); - // Remove annotation the declaration if exists e.g. @Annot String - for (AnnotationExpr expr : node.getAnnotations()) { - if (expr.equals(annotationExpr)) { - Optional annotRange = expr.getRange(); - annotRange - .map(value -> new Deletion(expr.toString(), value.begin, value.end)) - .ifPresent(modifications::add); - } - } - - // Remove annotation the type if exists e.g. java.lang.@Annot String - for (AnnotationExpr expr : type.getAnnotations()) { - if (expr.equals(annotationExpr)) { - Optional annotRange = expr.getRange(); - annotRange - .map(value -> new Deletion(expr.toString(), value.begin, value.end)) - .ifPresent(modifications::add); + // Remove the annotation from the declaration if exists e.g. @Annot String f; + Modification onDeclaration = super.computeTextModificationOn(node); + if (onDeclaration != null) { + modifications.add(onDeclaration); + } else { + // Remove the annotation from the type if exists e.g. java.lang.@Annot String f; + for (AnnotationExpr expr : type.getAnnotations()) { + if (expr.equals(annotationExpr)) { + Optional annotRange = expr.getRange(); + annotRange + .map(value -> new Deletion(expr.toString(), value.begin, value.end)) + .ifPresent(modifications::add); + } } } @@ -78,16 +73,17 @@ public & NodeWithRange> Modification comput ClassOrInterfaceType classOrInterfaceType = (ClassOrInterfaceType) type; if (classOrInterfaceType.getTypeArguments().isPresent()) { classOrInterfaceType - .getTypeArguments() - .get() - .forEach( - typeArg -> { - Modification onType = - computeTextModificationOn((NodeWithAnnotations & NodeWithRange) typeArg); - if (onType != null) { - modifications.add(onType); - } - }); + .getTypeArguments() + .get() + .forEach( + typeArg -> { + Modification onType = + computeTextModificationOn( + (NodeWithAnnotations & NodeWithRange) typeArg); + if (onType != null) { + modifications.add(onType); + } + }); } } return modifications.isEmpty() ? null : new MultiPositionModification(modifications); diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/MultiPositionModification.java b/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/MultiPositionModification.java index e429afe6c..3e16e0f96 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/MultiPositionModification.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/modifications/MultiPositionModification.java @@ -35,28 +35,28 @@ /** Represents a composite modification that consists of multiple modifications. */ public class MultiPositionModification implements Modification { - /** The modifications that should be applied to the source file in the reverse order. */ - final SortedSet modifications; - - public MultiPositionModification(Set modifications) { - // Modification are sorted to start from the last position, to make the changes ineffective to - // current computed offsets. - this.modifications = new TreeSet<>(Collections.reverseOrder()); - this.modifications.addAll(modifications); - } - - @Override - public void visit(List lines, FileOffsetStore offsetStore) { - this.modifications.forEach(modification -> modification.visit(lines, offsetStore)); - } - - @Override - public Position getStartingPosition() { - return modifications.last().getStartingPosition(); - } - - @Override - public int compareTo(Modification o) { - return COMPARATOR.compare(this, o); - } -} \ No newline at end of file + /** The modifications that should be applied to the source file in the reverse order. */ + final SortedSet modifications; + + public MultiPositionModification(Set modifications) { + // Modification are sorted to start from the last position, to make the changes ineffective to + // current computed offsets. + this.modifications = new TreeSet<>(Collections.reverseOrder()); + this.modifications.addAll(modifications); + } + + @Override + public void visit(List lines, FileOffsetStore offsetStore) { + this.modifications.forEach(modification -> modification.visit(lines, offsetStore)); + } + + @Override + public Position getStartingPosition() { + return modifications.last().getStartingPosition(); + } + + @Override + public int compareTo(Modification o) { + return COMPARATOR.compare(this, o); + } +} diff --git a/injector/src/test/java/edu/ucr/cs/riple/injector/TypeUseAnnotationTest.java b/injector/src/test/java/edu/ucr/cs/riple/injector/TypeUseAnnotationTest.java index a717a7998..1894a2534 100644 --- a/injector/src/test/java/edu/ucr/cs/riple/injector/TypeUseAnnotationTest.java +++ b/injector/src/test/java/edu/ucr/cs/riple/injector/TypeUseAnnotationTest.java @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022 Nima Karimipour + * Copyright (c) 2023 Nima Karimipour * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,5 +24,221 @@ package edu.ucr.cs.riple.injector; -public class TypeUseAnnotationTest { +import edu.ucr.cs.riple.injector.changes.AddTypeUseMarkerAnnotation; +import edu.ucr.cs.riple.injector.changes.RemoveTypeUseMarkerAnnotation; +import edu.ucr.cs.riple.injector.location.OnField; +import edu.ucr.cs.riple.injector.location.OnLocalVariable; +import edu.ucr.cs.riple.injector.location.OnMethod; +import edu.ucr.cs.riple.injector.location.OnParameter; +import java.util.Set; +import org.junit.Test; + +public class TypeUseAnnotationTest extends BaseInjectorTest { + + @Test + public void additionTest() { + injectorTestHelper + .addInput( + "Foo.java", + "package test;", + "public class Foo {", + " public void foo() {", + " int f0;", + " Bar> f1;", + " String f2;", + " }", + "}") + .expectOutput( + "package test;", + "import edu.ucr.UnTainted;", + "public class Foo {", + " public void foo() {", + " @UnTainted int f0;", + " @UnTainted Bar<@UnTainted String, @UnTainted Integer, @UnTainted Baz<@UnTainted String, @UnTainted Integer>> f1;", + " @UnTainted String f2;", + " }", + "}") + .addChanges( + new AddTypeUseMarkerAnnotation( + new OnLocalVariable("Foo.java", "test.Foo", "foo()", "f0"), "edu.ucr.UnTainted"), + new AddTypeUseMarkerAnnotation( + new OnLocalVariable("Foo.java", "test.Foo", "foo()", "f1"), "edu.ucr.UnTainted"), + new AddTypeUseMarkerAnnotation( + new OnLocalVariable("Foo.java", "test.Foo", "foo()", "f2"), "edu.ucr.UnTainted")) + .start(); + } + + @Test + public void deletionTest() { + injectorTestHelper + .addInput( + "Foo.java", + "package test;", + "import edu.ucr.UnTainted;", + "public class Foo {", + " public void foo() {", + " @UnTainted int f0;", + " @UnTainted Bar<@UnTainted String, @UnTainted Integer, @UnTainted Baz<@UnTainted String, @UnTainted Integer>> f1;", + " @UnTainted String f2;", + " }", + "}") + .expectOutput( + "package test;", + "import edu.ucr.UnTainted;", + "public class Foo {", + " public void foo() {", + " int f0;", + " Bar> f1;", + " String f2;", + " }", + "}") + .addChanges( + new RemoveTypeUseMarkerAnnotation( + new OnLocalVariable("Foo.java", "test.Foo", "foo()", "f0"), "edu.ucr.UnTainted"), + new RemoveTypeUseMarkerAnnotation( + new OnLocalVariable("Foo.java", "test.Foo", "foo()", "f1"), "edu.ucr.UnTainted"), + new RemoveTypeUseMarkerAnnotation( + new OnLocalVariable("Foo.java", "test.Foo", "foo()", "f2"), "edu.ucr.UnTainted")) + .start(); + } + + @Test + public void additionOnFullyQualifiedTypeNamesTest() { + injectorTestHelper + .addInput( + "Foo.java", + "package test;", + "public class Foo {", + " java.lang.Object bar;", + " java.lang.Object baz(java.lang.Object param) {;", + " java.lang.Object localVar;", + " return new Object();", + " }", + "}") + .expectOutput( + "package test;", + "import custom.example.Untainted;", + "public class Foo {", + " java.lang.@Untainted Object bar;", + " java.lang.@Untainted Object baz(java.lang.@Untainted Object param) {;", + " java.lang.@Untainted Object localVar;", + " return new Object();", + " }", + "}") + .addChanges( + new AddTypeUseMarkerAnnotation( + new OnField("Foo.java", "test.Foo", Set.of("bar")), "custom.example.Untainted"), + new AddTypeUseMarkerAnnotation( + new OnMethod("Foo.java", "test.Foo", "baz(java.lang.Object)"), + "custom.example.Untainted"), + new AddTypeUseMarkerAnnotation( + new OnLocalVariable("Foo.java", "test.Foo", "baz(java.lang.Object)", "localVar"), + "custom.example.Untainted"), + new AddTypeUseMarkerAnnotation( + new OnParameter("Foo.java", "test.Foo", "baz(java.lang.Object)", 0), + "custom.example.Untainted")) + .start(); + } + + @Test + public void deletionOnFullyQualifiedTypeNamesTest() { + injectorTestHelper + .addInput( + "Foo.java", + "package test;", + "import custom.example.Untainted;", + "public class Foo {", + " java.lang.@Untainted Object bar;", + " java.util.@Untainted Map f0;", + " java.lang.@Untainted Object baz(java.lang.@Untainted Object param) {;", + " java.lang.@Untainted Object localVar;", + " return new Object();", + " }", + "}") + .expectOutput( + "package test;", + "import custom.example.Untainted;", + "public class Foo {", + " java.lang.Object bar;", + " java.util.Map f0;", + " java.lang.Object baz(java.lang.Object param) {;", + " java.lang.Object localVar;", + " return new Object();", + " }", + "}") + .addChanges( + new RemoveTypeUseMarkerAnnotation( + new OnField("Foo.java", "test.Foo", Set.of("bar")), "custom.example.Untainted"), + new RemoveTypeUseMarkerAnnotation( + new OnField("Foo.java", "test.Foo", Set.of("f0")), "custom.example.Untainted"), + new RemoveTypeUseMarkerAnnotation( + new OnMethod("Foo.java", "test.Foo", "baz(java.lang.Object)"), + "custom.example.Untainted"), + new RemoveTypeUseMarkerAnnotation( + new OnLocalVariable("Foo.java", "test.Foo", "baz(java.lang.Object)", "localVar"), + "custom.example.Untainted"), + new RemoveTypeUseMarkerAnnotation( + new OnParameter("Foo.java", "test.Foo", "baz(java.lang.Object)", 0), + "custom.example.Untainted")) + .start(); + } + + @Test + public void additionOnArrayTest() { + injectorTestHelper + .addInput( + "Foo.java", + "package test;", + "public class Foo {", + " public void foo() {", + " java.util.Map f0;", + " T[] f1;", + " String[] f2;", + " }", + "}") + .expectOutput( + "package test;", + "import edu.ucr.UnTainted;", + "public class Foo {", + " public void foo() {", + " java.util.@UnTainted Map f0;", + " @UnTainted T[] f1;", + " @UnTainted String[] f2;", + " }", + "}") + .addChanges( + new AddTypeUseMarkerAnnotation( + new OnLocalVariable("Foo.java", "test.Foo", "foo()", "f0"), "edu.ucr.UnTainted"), + new AddTypeUseMarkerAnnotation( + new OnLocalVariable("Foo.java", "test.Foo", "foo()", "f1"), "edu.ucr.UnTainted"), + new AddTypeUseMarkerAnnotation( + new OnLocalVariable("Foo.java", "test.Foo", "foo()", "f2"), "edu.ucr.UnTainted")) + .start(); + } + + @Test + public void deletionOnArrayTest() { + injectorTestHelper + .addInput( + "Foo.java", + "package test;", + "import custom.example.Untainted;", + "public class Foo {", + " java.util.Map f0;", + " java.util.@Untainted Map<@Untainted String, @Untainted String[]> f1;", + "}") + .expectOutput( + "package test;", + "import custom.example.Untainted;", + "public class Foo {", + " java.util.@Untainted Map<@Untainted String, @Untainted String[]> f0;", + " java.util.Map f1;", + "}") + .addChanges( + new AddTypeUseMarkerAnnotation( + new OnField("Foo.java", "test.Foo", Set.of("f0")), "custom.example.Untainted"), + new RemoveTypeUseMarkerAnnotation( + new OnField("Foo.java", "test.Foo", Set.of("f1")), "custom.example.Untainted")) + .start(); + } } From 97a9ea4951c78df8dfe5766fa3452170e4d0333c Mon Sep 17 00:00:00 2001 From: nimakarimipour Date: Tue, 9 May 2023 17:34:05 -0700 Subject: [PATCH 17/35] add javadoc --- .../changes/AddTypeUseMarkerAnnotation.java | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddTypeUseMarkerAnnotation.java b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddTypeUseMarkerAnnotation.java index 23ac29c74..9108d2f72 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddTypeUseMarkerAnnotation.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddTypeUseMarkerAnnotation.java @@ -95,6 +95,26 @@ public RemoveAnnotation getReverse() { return new RemoveTypeUseMarkerAnnotation(location, annotationName.fullName); } + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof AddTypeUseMarkerAnnotation)) { + return false; + } + AddTypeUseMarkerAnnotation otherAdd = (AddTypeUseMarkerAnnotation) other; + return this.location.equals(otherAdd.location) + && this.annotationName.equals(otherAdd.annotationName); + } + + /** + * Find the range of the given type in source code. This is used to insert the annotation before + * the type. + * + * @param type the type to find its range + * @return the range of the type or null if the type does not have a range. + */ private static Range findTypeRange(Type type) { if (type instanceof ClassOrInterfaceType) { ClassOrInterfaceType classOrInterfaceType = (ClassOrInterfaceType) type; @@ -115,17 +135,4 @@ private static Range findTypeRange(Type type) { throw new RuntimeException( "Unexpected type to get range from: " + type + " : " + type.getClass()); } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!(other instanceof AddTypeUseMarkerAnnotation)) { - return false; - } - AddTypeUseMarkerAnnotation otherAdd = (AddTypeUseMarkerAnnotation) other; - return this.location.equals(otherAdd.location) - && this.annotationName.equals(otherAdd.annotationName); - } } From 68316e902435ee754277f765a2dca361987ff166 Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Wed, 10 May 2023 14:33:02 -0700 Subject: [PATCH 18/35] add javadoc --- .../injector/changes/AddTypeUseMarkerAnnotation.java | 10 +++++++++- .../changes/RemoveTypeUseMarkerAnnotation.java | 9 +++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddTypeUseMarkerAnnotation.java b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddTypeUseMarkerAnnotation.java index 23ac29c74..e407d49ca 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddTypeUseMarkerAnnotation.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/AddTypeUseMarkerAnnotation.java @@ -40,7 +40,15 @@ import java.util.Set; import javax.annotation.Nullable; -/** Used to add type-use marker annotations on elements in source code. */ +/** + * Adds a type-use marker annotation to a node in the source code. It will add the annotation to + * both the declaration and all type arguments. + * + *

For instance, for the node: {@code java.util.Map list;} It will add + * the {@code @Nullable} annotation to the type {@code Map} and all the type arguments. The final + * output will be: {@code java.util.@Nullable Map<@Nullable String, java.lang.@Nullable String> + * list;} + */ public class AddTypeUseMarkerAnnotation extends AddMarkerAnnotation { public AddTypeUseMarkerAnnotation(Location location, String annotation) { diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/RemoveTypeUseMarkerAnnotation.java b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/RemoveTypeUseMarkerAnnotation.java index eb28a1a42..a66302789 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/changes/RemoveTypeUseMarkerAnnotation.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/changes/RemoveTypeUseMarkerAnnotation.java @@ -39,6 +39,15 @@ import java.util.Set; import javax.annotation.Nullable; +/** + * Removes a type-use marker annotation from a node in the source code. It will remove the + * annotation both from the declaration and all type arguments. + * + *

For instance, for the node: {@code java.util.@Nullable Map<@Nullable String, + * java.lang.@Nullable String> list;} It will remove the {@code @Nullable} annotation from the type + * {@code Map} and all the type arguments. The final output will be: {@code java.util.Map list;} + */ public class RemoveTypeUseMarkerAnnotation extends RemoveMarkerAnnotation { public RemoveTypeUseMarkerAnnotation(Location location, String annotation) { From 4170c9dce408c03fb6ba6f3cb11f4fb865620078 Mon Sep 17 00:00:00 2001 From: nimakarimipour Date: Wed, 17 May 2023 00:35:58 -0700 Subject: [PATCH 19/35] init --- .../java/edu/ucr/cs/riple/core/Annotator.java | 151 +------ .../java/edu/ucr/cs/riple/core/Config.java | 9 +- .../java/edu/ucr/cs/riple/core/Context.java | 38 +- .../ucr/cs/riple/core/checkers/Checker.java | 89 ++++ .../riple/core/checkers/CheckerBaseClass.java | 113 +++++ .../nullaway/FixSerializationConfig.java | 170 ++++++++ .../core/checkers/nullaway/NullAway.java | 395 ++++++++++++++++++ .../core/checkers/nullaway/NullAwayError.java | 96 +++++ .../cs/riple/core/metadata/index/Error.java | 9 +- .../cs/riple/core/metadata/index/Index.java | 2 +- .../core/module/ModuleConfiguration.java | 12 +- .../ucr/cs/riple/core/module/ModuleInfo.java | 23 +- .../edu/ucr/cs/riple/core/util/Utility.java | 18 +- .../ucr/cs/riple/core/ConfigurationTest.java | 7 +- 14 files changed, 920 insertions(+), 212 deletions(-) create mode 100644 annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java create mode 100644 annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java create mode 100644 annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/FixSerializationConfig.java create mode 100644 annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java create mode 100644 annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAwayError.java diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Annotator.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Annotator.java index 58767c3a0..9bcb4f262 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Annotator.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Annotator.java @@ -38,15 +38,11 @@ import edu.ucr.cs.riple.core.injectors.AnnotationInjector; import edu.ucr.cs.riple.core.injectors.PhysicalInjector; import edu.ucr.cs.riple.core.metadata.field.FieldInitializationStore; -import edu.ucr.cs.riple.core.metadata.index.Error; import edu.ucr.cs.riple.core.metadata.index.Fix; import edu.ucr.cs.riple.core.util.Utility; import edu.ucr.cs.riple.injector.changes.AddAnnotation; import edu.ucr.cs.riple.injector.changes.AddMarkerAnnotation; -import edu.ucr.cs.riple.injector.changes.AddSingleElementAnnotation; import edu.ucr.cs.riple.injector.location.OnField; -import edu.ucr.cs.riple.injector.location.OnParameter; -import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -138,7 +134,7 @@ private void annotate() { } } if (config.forceResolveActivated) { - forceResolveRemainingErrors(); + context.checker.suppressRemainingAnnotations(injector); } System.out.println("\nFinished annotating."); Utility.writeReports(context, cache.reports().stream().collect(ImmutableSet.toImmutableSet())); @@ -219,149 +215,4 @@ private Evaluator getEvaluator(Supplier supplier) { } return new BasicEvaluator(supplier); } - - /** - * Resolves all remaining errors in target module by following steps below: - * - *

- */ - private void forceResolveRemainingErrors() { - // Collect regions with remaining errors. - Utility.buildTarget(context); - Set remainingErrors = - Utility.readErrorsFromOutputDirectory(context, context.targetModuleInfo); - Set remainingFixes = - Utility.readFixesFromOutputDirectory(context, context.targetModuleInfo); - // Collect all regions for NullUnmarked. - // For all errors in regions which correspond to a method's body, we can add @NullUnmarked at - // the method level. - Set nullUnMarkedAnnotations = - remainingErrors.stream() - // find the corresponding method nodes. - .map( - error -> { - if (error.getRegion().isOnCallable() - && - // We suppress initialization errors reported on constructors using - // @SuppressWarnings("NullAway.Init"). We add @NullUnmarked on constructors - // only for errors in the body of the constructor. - !error.isInitializationError()) { - return context - .targetModuleInfo - .getMethodRegistry() - .findMethodByName(error.encClass(), error.encMember()); - } - // For methods invoked in an initialization region, where the error is that - // `@Nullable` is being passed as an argument, we add a `@NullUnmarked` annotation - // to the called method. - if (error.messageType.equals("PASS_NULLABLE") - && error.isSingleFix() - && error.toResolvingLocation().isOnParameter()) { - OnParameter nullableParameter = error.toResolvingParameter(); - return context - .targetModuleInfo - .getMethodRegistry() - .findMethodByName( - nullableParameter.clazz, nullableParameter.enclosingMethod.method); - } - return null; - }) - // Filter null values from map above. - .filter(Objects::nonNull) - .map(node -> new AddMarkerAnnotation(node.location, config.nullUnMarkedAnnotation)) - .collect(Collectors.toSet()); - - // For errors within static initialization blocks, add a @NullUnmarked annotation on the - // enclosing class - nullUnMarkedAnnotations.addAll( - remainingErrors.stream() - .filter( - error -> - error.getRegion().isOnInitializationBlock() - && !error.getRegion().isInAnonymousClass()) - .map( - error -> - new AddMarkerAnnotation( - context.targetModuleInfo.getLocationOnClass(error.getRegion().clazz), - config.nullUnMarkedAnnotation)) - .collect(Collectors.toSet())); - injector.injectAnnotations(nullUnMarkedAnnotations); - // Update log. - context.log.updateInjectedAnnotations(nullUnMarkedAnnotations); - - // Collect suppress warnings, errors on field declaration regions. - Set fieldsWithSuppressWarnings = - remainingErrors.stream() - .filter( - error -> { - if (!error.getRegion().isOnField()) { - return false; - } - if (error.messageType.equals("PASS_NULLABLE")) { - // It is already resolved with @NullUnmarked selected above. - return false; - } - // We can silence them by SuppressWarnings("NullAway.Init") - return !error.isInitializationError(); - }) - .map( - error -> - context - .targetModuleInfo - .getFieldRegistry() - .getLocationOnField(error.getRegion().clazz, error.getRegion().member)) - .filter(Objects::nonNull) - .collect(Collectors.toSet()); - - Set suppressWarningsAnnotations = - fieldsWithSuppressWarnings.stream() - .map( - onField -> - new AddSingleElementAnnotation(onField, "SuppressWarnings", "NullAway", false)) - .collect(Collectors.toSet()); - injector.injectAnnotations(suppressWarningsAnnotations); - // Update log. - context.log.updateInjectedAnnotations(suppressWarningsAnnotations); - - // Collect NullAway.Init SuppressWarnings - Set initializationSuppressWarningsAnnotations = - remainingFixes.stream() - .filter( - fix -> - fix.isOnField() - && (fix.reasons.contains("METHOD_NO_INIT") - || fix.reasons.contains("FIELD_NO_INIT"))) - // Filter nodes annotated with SuppressWarnings("NullAway") - .filter(fix -> !fieldsWithSuppressWarnings.contains(fix.toField())) - .map( - fix -> - new AddSingleElementAnnotation( - fix.toField(), "SuppressWarnings", "NullAway.Init", false)) - .collect(Collectors.toSet()); - injector.injectAnnotations(initializationSuppressWarningsAnnotations); - // Update log. - context.log.updateInjectedAnnotations(initializationSuppressWarningsAnnotations); - // Collect @NullUnmarked annotations on classes for any remaining error. - Utility.buildTarget(context); - remainingErrors = Utility.readErrorsFromOutputDirectory(context, context.targetModuleInfo); - nullUnMarkedAnnotations = - remainingErrors.stream() - .filter(error -> !error.getRegion().isInAnonymousClass()) - .map( - error -> - new AddMarkerAnnotation( - context.targetModuleInfo.getLocationOnClass(error.getRegion().clazz), - config.nullUnMarkedAnnotation)) - .collect(Collectors.toSet()); - injector.injectAnnotations(nullUnMarkedAnnotations); - // Update log. - context.log.updateInjectedAnnotations(nullUnMarkedAnnotations); - } } diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Config.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Config.java index 4ff32b10a..1f24834a6 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Config.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Config.java @@ -148,7 +148,7 @@ public class Config { */ public final ImmutableSet generatedCodeDetectors; /** Checker enum to retrieve checker specific instances. (e.g. {@link CheckerDeserializer}) */ - public final Checker checker; + public final String checker; /** * Builds context from command line arguments. @@ -381,7 +381,7 @@ public Config(String[] args) { cmd.hasOption(nullableOption.getLongOpt()) ? cmd.getOptionValue(nullableOption.getLongOpt()) : "javax.annotation.Nullable"; - this.checker = Checker.getCheckerByName(cmd.getOptionValue(checkerNameOption)); + this.checker = cmd.getOptionValue(checkerNameOption); this.initializerAnnot = cmd.getOptionValue(initializerOption.getLongOpt()); this.depth = Integer.parseInt( @@ -467,8 +467,7 @@ public Config(Path configPath) { } catch (Exception e) { throw new RuntimeException("Error in reading/parsing context at path: " + configPath, e); } - this.checker = - Checker.getCheckerByName(getValueFromKey(jsonObject, "CHECKER", String.class).orElse(null)); + this.checker = getValueFromKey(jsonObject, "CHECKER", String.class).orElse(null); this.depth = getValueFromKey(jsonObject, "DEPTH", Long.class).orElse((long) 1).intValue(); this.chain = getValueFromKey(jsonObject, "CHAIN", Boolean.class).orElse(false); this.redirectBuildOutputToStdErr = @@ -712,7 +711,7 @@ public void write(Path path) { .map( info -> { JSONObject res = new JSONObject(); - res.put("NULLAWAY", info.nullawayConfig.toString()); + res.put("NULLAWAY", info.checkerConfig.toString()); res.put("SCANNER", info.scannerConfig.toString()); return res; }) diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java index 3f1ffadd7..d35df2249 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java @@ -25,16 +25,17 @@ package edu.ucr.cs.riple.core; import com.google.common.collect.ImmutableSet; +import edu.ucr.cs.riple.core.checkers.Checker; +import edu.ucr.cs.riple.core.checkers.CheckerBaseClass; import edu.ucr.cs.riple.core.io.deserializers.CheckerDeserializer; import edu.ucr.cs.riple.core.log.Log; +import edu.ucr.cs.riple.core.metadata.index.Error; import edu.ucr.cs.riple.core.module.ModuleConfiguration; import edu.ucr.cs.riple.core.module.ModuleInfo; -import edu.ucr.cs.riple.core.util.Utility; import edu.ucr.cs.riple.injector.offsets.FileOffsetStore; import edu.ucr.cs.riple.injector.offsets.OffsetChange; import java.nio.file.Path; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Set; @@ -60,10 +61,8 @@ public class Context { public final ModuleConfiguration targetConfiguration; /** Sets of context path information for all downstream dependencies. */ public final ImmutableSet downstreamConfigurations; - /** Deserializer for reading the output of the checker. */ - public final CheckerDeserializer deserializer; /** Checker enum to retrieve checker specific instances. (e.g. {@link CheckerDeserializer}) */ - public final Checker checker; + public final Checker checker; /** * Builds context from command line arguments. @@ -72,41 +71,14 @@ public class Context { */ public Context(Config config) { this.config = config; - this.checker = config.checker; + this.checker = CheckerBaseClass.getCheckerByName(config.checker, this); this.offsetHandler = new OffsetHandler(); this.downstreamConfigurations = config.downstreamConfigurations; this.log = new Log(); this.targetConfiguration = config.target; - this.deserializer = initializeCheckerDeserializer(); this.targetModuleInfo = new ModuleInfo(this, config.target, config.buildCommand); } - /** - * Initializes the checker deserializer based on the checker name and the serialization version. - * - * @return the checker deserializer associated with the requested checker name and version. - */ - public CheckerDeserializer initializeCheckerDeserializer() { - // To retrieve the serialization version, we need to build the target first. - Utility.buildTarget(this); - Path serializationVersionPath = config.target.dir.resolve("serialization_version.txt"); - if (!serializationVersionPath.toFile().exists()) { - // Older versions of checkers - throw new RuntimeException( - "Serialization version not found. Upgrade to newer versions of the checkers: " - + checker.name()); - } - List lines = Utility.readFileLines(serializationVersionPath); - int version = Integer.parseInt(lines.get(0)); - CheckerDeserializer deserializer = checker.getDeserializer(this); - if (deserializer.getVersionNumber() == version) { - return deserializer; - } else { - throw new RuntimeException( - "Serialization version mismatch. Upgrade new versions of checkers."); - } - } - /** Responsible for handling offset changes in source file. */ public static class OffsetHandler { diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java new file mode 100644 index 000000000..5f6bce13d --- /dev/null +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java @@ -0,0 +1,89 @@ +/* + * MIT License + * + * Copyright (c) 2022 Nima Karimipour + * + * 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.core.checkers; + +import com.google.common.collect.ImmutableSet; +import edu.ucr.cs.riple.core.injectors.AnnotationInjector; +import edu.ucr.cs.riple.core.metadata.index.Error; +import edu.ucr.cs.riple.core.metadata.index.Fix; +import edu.ucr.cs.riple.core.metadata.trackers.Region; +import edu.ucr.cs.riple.core.module.ModuleInfo; +import java.util.Set; + +/** + * Represents a checker that is running on the target module. + * + * @param Type of errors reported by the checker. + */ +public interface Checker { + + /** + * Deserializes errors reported by the checker from the output using the given context. + * + * @param module Module where the checker reports errors. + * @return Set of errors reported by the checker. + */ + Set deserializeErrors(ModuleInfo module); + + /** + * Suppresses remaining errors reported by the checker. + * + * @param context Annotator context. + * @param injector Annotation injector to inject selected annotations. + */ + void suppressRemainingAnnotations(AnnotationInjector injector); + + /** + * Creates an {@link Error} instance from the given parameters. + * + * @param errorType Error type. + * @param errorMessage Error message. + * @param region Region where the error is reported, + * @param offset offset of program point in original version where error is reported. + * @param resolvingFixes Set of fixes that resolve the error. + * @return The corresponding error. + */ + T createErrorFactory( + String errorType, + String errorMessage, + Region region, + int offset, + ImmutableSet resolvingFixes); + + /** + * Verifies that the checker representation in Annotator is compatible with the actual running + * checker on the target module. + * + * @param version The version of the actual running checker. + */ + void verifyCheckerCompatibility(int version); + + /** + * Prepares the config files for the checker to run on the target module. + * + * @param module Module where its config files should be prepared for a build. + */ + void prepareConfigFilesForBuild(ModuleInfo module); +} diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java new file mode 100644 index 000000000..ab25760df --- /dev/null +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java @@ -0,0 +1,113 @@ +/* + * MIT License + * + * Copyright (c) 2022 Nima Karimipour + * + * 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.core.checkers; + +import com.google.common.collect.ImmutableSet; +import edu.ucr.cs.riple.core.Config; +import edu.ucr.cs.riple.core.Context; +import edu.ucr.cs.riple.core.checkers.nullaway.NullAway; +import edu.ucr.cs.riple.core.metadata.index.Error; +import edu.ucr.cs.riple.core.metadata.index.Fix; +import edu.ucr.cs.riple.core.metadata.trackers.Region; +import edu.ucr.cs.riple.core.module.ModuleInfo; +import edu.ucr.cs.riple.injector.location.OnField; +import java.util.Set; + +/** Base class for all checker representations. */ +public abstract class CheckerBaseClass implements Checker { + + /** Annotator config. */ + protected final Config config; + /** The supporting serialization version of this deserializer. */ + protected final int version; + /** Annotator context. */ + protected final Context context; + + public CheckerBaseClass(Context context, int version) { + this.context = context; + this.config = context.config; + this.version = version; + } + + /** + * Creates an {@link Error} instance. + * + * @param errorType Error type. + * @param errorMessage Error message. + * @param region Region where the error is reported, + * @param offset offset of program point in original version where error is reported. + * @param resolvingFixes Set of fixes that resolve the error. + * @param module Module where the error is reported. + * @return The corresponding error. + */ + public T createError( + String errorType, + String errorMessage, + Region region, + int offset, + ImmutableSet resolvingFixes, + ModuleInfo module) { + ImmutableSet fixes = + resolvingFixes.stream() + .filter(f -> !module.getNonnullStore().hasExplicitNonnullAnnotation(f.toLocation())) + .collect(ImmutableSet.toImmutableSet()); + return createErrorFactory(errorType, errorMessage, region, offset, fixes); + } + + /** + * Extends field variable names to full list to include all variables declared in the same + * statement. + * + * @param onField Location of the field. + * @return The updated given location. + */ + protected static OnField extendVariableList(OnField onField, ModuleInfo moduleInfo) { + Set variables = + moduleInfo + .getFieldRegistry() + .getInLineMultipleFieldDeclarationsOnField(onField.clazz, onField.variables); + onField.variables.addAll(variables); + return onField; + } + + /** + * Returns the checker instance by its name. + * + * @param name name of the checker. + * @param config annotator config. + * @return the checker instance with the given name. + */ + public static Checker getCheckerByName(String name, Context context) { + if (name == null) { + throw new RuntimeException("Checker name is null"); + } + switch (name) { + case NullAway.NAME: + return new NullAway(context); + default: + throw new RuntimeException("Unknown checker name: " + name); + } + } +} diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/FixSerializationConfig.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/FixSerializationConfig.java new file mode 100644 index 000000000..fc0bd782d --- /dev/null +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/FixSerializationConfig.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2022 Uber Technologies, Inc. + * + * 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.core.checkers.nullaway; + +import java.io.File; +import java.util.UUID; +import javax.annotation.Nullable; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * IMPORTANT NOTE: THIS CLASS IS COPIED FROM NULLAWAY, WE COPIED THE CLASS CONTENT HERE TO REMOVE + * DEPENDENCY TO NULLAWAY. + * + *

Config class for Fix Serialization package. + */ +public class FixSerializationConfig { + + public final boolean suggestEnabled; + + public final boolean suggestEnclosing; + + public final boolean fieldInitInfoEnabled; + + /** The directory where all files generated/read by Fix Serialization package resides. */ + @Nullable public final String outputDirectory; + + /** Default Constructor, all features are disabled with this config. */ + public FixSerializationConfig() { + suggestEnabled = false; + suggestEnclosing = false; + fieldInitInfoEnabled = false; + outputDirectory = null; + } + + public FixSerializationConfig( + boolean suggestEnabled, + boolean suggestEnclosing, + boolean fieldInitInfoEnabled, + String outputDirectory) { + this.suggestEnabled = suggestEnabled; + this.suggestEnclosing = suggestEnclosing; + this.fieldInitInfoEnabled = fieldInitInfoEnabled; + this.outputDirectory = outputDirectory; + } + + /** Builder class for Serialization Config */ + public static class Builder { + + private boolean suggestEnabled; + private boolean suggestEnclosing; + private boolean fieldInitInfo; + @Nullable private String outputDir; + + public Builder() { + suggestEnabled = false; + suggestEnclosing = false; + fieldInitInfo = false; + } + + public Builder setSuggest(boolean value, boolean withEnclosing) { + this.suggestEnabled = value; + this.suggestEnclosing = withEnclosing && suggestEnabled; + return this; + } + + public Builder setFieldInitInfo(boolean enabled) { + this.fieldInitInfo = enabled; + return this; + } + + public Builder setOutputDirectory(String outputDir) { + this.outputDir = outputDir; + return this; + } + + /** + * Builds and writes the config with the state in builder at the given path as XML. + * + * @param path path to write the config file. + */ + public void writeAsXML(String path) { + FixSerializationConfig config = this.build(); + writeNullAwayConfigInXMLFormat(config, path); + } + + public FixSerializationConfig build() { + if (outputDir == null) { + throw new IllegalStateException("did not set mandatory output directory"); + } + return new FixSerializationConfig(suggestEnabled, suggestEnclosing, fieldInitInfo, outputDir); + } + + /** + * Writes the {@link FixSerializationConfig} in {@code XML} format. + * + * @param config Config file to write. + * @param path Path to write the config at. + */ + public static void writeNullAwayConfigInXMLFormat(FixSerializationConfig config, String path) { + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + try { + DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + Document doc = docBuilder.newDocument(); + + // Root + Element rootElement = doc.createElement("serialization"); + doc.appendChild(rootElement); + + // Suggest + Element suggestElement = doc.createElement("suggest"); + suggestElement.setAttribute("active", String.valueOf(config.suggestEnabled)); + suggestElement.setAttribute("enclosing", String.valueOf(config.suggestEnclosing)); + rootElement.appendChild(suggestElement); + + // Field Initialization + Element fieldInitInfoEnabled = doc.createElement("fieldInitInfo"); + fieldInitInfoEnabled.setAttribute("active", String.valueOf(config.fieldInitInfoEnabled)); + rootElement.appendChild(fieldInitInfoEnabled); + + // Output dir + Element outputDir = doc.createElement("path"); + outputDir.setTextContent(config.outputDirectory); + rootElement.appendChild(outputDir); + + // UUID + Element uuid = doc.createElement("uuid"); + uuid.setTextContent(UUID.randomUUID().toString()); + rootElement.appendChild(uuid); + + // Writings + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + DOMSource source = new DOMSource(doc); + StreamResult result = new StreamResult(new File(path)); + transformer.transform(source, result); + } catch (ParserConfigurationException | TransformerException e) { + throw new RuntimeException("Error happened in writing config.", e); + } + } + } +} diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java new file mode 100644 index 000000000..c824f6de8 --- /dev/null +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java @@ -0,0 +1,395 @@ +/* + * MIT License + * + * Copyright (c) 2022 Nima Karimipour + * + * 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.core.checkers.nullaway; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import edu.ucr.cs.riple.core.Context; +import edu.ucr.cs.riple.core.checkers.CheckerBaseClass; +import edu.ucr.cs.riple.core.injectors.AnnotationInjector; +import edu.ucr.cs.riple.core.metadata.index.Fix; +import edu.ucr.cs.riple.core.metadata.trackers.Region; +import edu.ucr.cs.riple.core.module.ModuleInfo; +import edu.ucr.cs.riple.core.util.Utility; +import edu.ucr.cs.riple.injector.Helper; +import edu.ucr.cs.riple.injector.changes.AddAnnotation; +import edu.ucr.cs.riple.injector.changes.AddMarkerAnnotation; +import edu.ucr.cs.riple.injector.changes.AddSingleElementAnnotation; +import edu.ucr.cs.riple.injector.location.Location; +import edu.ucr.cs.riple.injector.location.OnField; +import edu.ucr.cs.riple.injector.location.OnParameter; +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** Represents NullAway checker in Annotator. */ +public class NullAway extends CheckerBaseClass { + + /** + * The name of the checker. To select this checker, this name must be used in the configurations. + */ + public static final String NAME = "NULLAWAY"; + /** Supported version of NullAway serialization. */ + public static final int VERSION = 3; + + public NullAway(Context context) { + super(context, 3); + } + + @Override + public Set deserializeErrors(ModuleInfo module) { + ImmutableSet paths = + module.getModuleConfiguration().stream() + .map(configuration -> configuration.dir.resolve("errors.tsv")) + .collect(ImmutableSet.toImmutableSet()); + Set errors = new HashSet<>(); + paths.forEach( + path -> { + try { + try (BufferedReader br = Files.newBufferedReader(path, Charset.defaultCharset())) { + String line; + // Skip header. + br.readLine(); + while ((line = br.readLine()) != null) { + errors.add(deserializeErrorFromTSVLine(module, line)); + } + } + } catch (IOException e) { + throw new RuntimeException("Exception happened in reading errors at: " + path, e); + } + }); + return errors; + } + + /** + * Deserializes an error from a TSV line. + * + * @param moduleInfo Module info. + * @param line Given TSV line. + * @return the deserialized error corresponding to the values in the given tsv line. + */ + private NullAwayError deserializeErrorFromTSVLine(ModuleInfo moduleInfo, String line) { + Context context = moduleInfo.getContext(); + String[] values = line.split("\t"); + Preconditions.checkArgument( + values.length == 12, + "Expected 12 values to create Error instance in NullAway serialization version 2 but found: " + + values.length); + int offset = Integer.parseInt(values[4]); + Path path = Helper.deserializePath(values[5]); + String errorMessage = values[1]; + String errorType = values[0]; + Region region = new Region(values[2], values[3]); + Location nonnullTarget = + Location.createLocationFromArrayInfo(Arrays.copyOfRange(values, 6, 12)); + if (nonnullTarget == null && errorType.equals(NullAwayError.METHOD_INITIALIZER_ERROR)) { + ImmutableSet resolvingFixes = + generateFixesForUninitializedFields(errorMessage, region, moduleInfo).stream() + .filter( + fix -> + !moduleInfo.getNonnullStore().hasExplicitNonnullAnnotation(fix.toLocation())) + .collect(ImmutableSet.toImmutableSet()); + return createError( + errorType, + errorMessage, + region, + context.offsetHandler.getOriginalOffset(path, offset), + resolvingFixes, + moduleInfo); + } + if (nonnullTarget != null && nonnullTarget.isOnField()) { + nonnullTarget = extendVariableList(nonnullTarget.toField(), moduleInfo); + } + Fix resolvingFix = + nonnullTarget == null + ? null + : (moduleInfo.getNonnullStore().hasExplicitNonnullAnnotation(nonnullTarget) + // skip if element has explicit nonnull annotation. + ? null + : new Fix( + new AddMarkerAnnotation(nonnullTarget, config.nullableAnnot), errorType, true)); + return createError( + errorType, + errorMessage, + region, + context.offsetHandler.getOriginalOffset(path, offset), + resolvingFix == null ? ImmutableSet.of() : ImmutableSet.of(resolvingFix), + moduleInfo); + } + + /** + * Extracts uninitialized field names from the given error message. + * + * @param errorMessage Error message. + * @return Set of uninitialized field names. + */ + private Set extractUninitializedFieldNames(String errorMessage) { + String prefix = "initializer method does not guarantee @NonNull field"; + int begin = prefix.length(); + if (errorMessage.charAt(begin) == 's') { + begin += 1; + } + int end = errorMessage.indexOf(" is initialized along"); + end = end == -1 ? errorMessage.indexOf(" are initialized along ") : end; + if (end == -1) { + throw new RuntimeException( + "Error message for initializer error not recognized in version " + + 3 + + ": " + + errorMessage); + } + String[] fieldsData = errorMessage.substring(begin, end).split(","); + Set fields = + Arrays.stream(fieldsData) + // NullAway serializes line number right after a field name starting with an open + // parentheses. (e.g. foo (line z)). This approach of extracting field names is + // extremely dependent on the format of NullAway error messages. Should be watched + // carefully and updated if the format is changed by NullAway (maybe regex?). + .map(s -> s.substring(0, s.indexOf("(")).trim()) + .collect(Collectors.toSet()); + if (fields.size() == 0) { + throw new RuntimeException( + "Could not extract any uninitialized field in message for initializer error in version " + + 3 + + ": " + + errorMessage); + } + return fields; + } + + /** + * Generates a set of fixes for uninitialized fields from the given error message. + * + * @param errorMessage Given error message. + * @param region Region where the error is reported. + * @return Set of fixes for uninitialized fields to resolve the given error. + */ + protected ImmutableSet generateFixesForUninitializedFields( + String errorMessage, Region region, ModuleInfo module) { + return extractUninitializedFieldNames(errorMessage).stream() + .map( + field -> { + OnField locationOnField = + module.getFieldRegistry().getLocationOnField(region.clazz, field); + if (locationOnField == null) { + return null; + } + return new Fix( + new AddMarkerAnnotation( + extendVariableList(locationOnField, module), config.nullableAnnot), + NullAwayError.METHOD_INITIALIZER_ERROR, + true); + }) + .filter(Objects::nonNull) + .collect(ImmutableSet.toImmutableSet()); + } + + /** + * Suppresses remaining errors by following steps below: + * + *

    + *
  • Enclosing method of triggered errors will be marked with {@code @NullUnmarked} + * annotation. + *
  • Uninitialized fields (inline or by constructor) will be annotated as + * {@code @SuppressWarnings("NullAway.Init")}. + *
  • Explicit {@code Nullable} assignments to fields will be annotated as + * {@code @SuppressWarnings("NullAway")}. + *
+ * + * @param injector Annotation injector to inject selected annotations. + */ + @Override + public void suppressRemainingAnnotations(AnnotationInjector injector) { + // Collect regions with remaining errors. + Utility.buildTarget(context); + Set remainingErrors = deserializeErrors(context.targetModuleInfo); + Set remainingFixes = + Utility.readFixesFromOutputDirectory(context, context.targetModuleInfo); + // Collect all regions for NullUnmarked. + // For all errors in regions which correspond to a method's body, we can add @NullUnmarked at + // the method level. + Set nullUnMarkedAnnotations = + remainingErrors.stream() + // find the corresponding method nodes. + .map( + error -> { + if (error.getRegion().isOnCallable() + && + // We suppress initialization errors reported on constructors using + // @SuppressWarnings("NullAway.Init"). We add @NullUnmarked on constructors + // only for errors in the body of the constructor. + error.isNonInitializationError()) { + return context + .targetModuleInfo + .getMethodRegistry() + .findMethodByName(error.encClass(), error.encMember()); + } + // For methods invoked in an initialization region, where the error is that + // `@Nullable` is being passed as an argument, we add a `@NullUnmarked` annotation + // to the called method. + if (error.messageType.equals("PASS_NULLABLE") + && error.isSingleFix() + && error.toResolvingLocation().isOnParameter()) { + OnParameter nullableParameter = error.toResolvingParameter(); + return context + .targetModuleInfo + .getMethodRegistry() + .findMethodByName( + nullableParameter.clazz, nullableParameter.enclosingMethod.method); + } + return null; + }) + // Filter null values from map above. + .filter(Objects::nonNull) + .map(node -> new AddMarkerAnnotation(node.location, config.nullUnMarkedAnnotation)) + .collect(Collectors.toSet()); + + // For errors within static initialization blocks, add a @NullUnmarked annotation on the + // enclosing class + nullUnMarkedAnnotations.addAll( + remainingErrors.stream() + .filter( + error -> + error.getRegion().isOnInitializationBlock() + && !error.getRegion().isInAnonymousClass()) + .map( + error -> + new AddMarkerAnnotation( + context.targetModuleInfo.getLocationOnClass(error.getRegion().clazz), + config.nullUnMarkedAnnotation)) + .collect(Collectors.toSet())); + Set result = new HashSet<>(nullUnMarkedAnnotations); + + // Collect suppress warnings, errors on field declaration regions. + Set fieldsWithSuppressWarnings = + remainingErrors.stream() + .filter( + error -> { + if (!error.getRegion().isOnField()) { + return false; + } + if (error.messageType.equals("PASS_NULLABLE")) { + // It is already resolved with @NullUnmarked selected above. + return false; + } + // We can silence them by SuppressWarnings("NullAway.Init") + return error.isNonInitializationError(); + }) + .map( + error -> + context + .targetModuleInfo + .getFieldRegistry() + .getLocationOnField(error.getRegion().clazz, error.getRegion().member)) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + Set suppressWarningsAnnotations = + fieldsWithSuppressWarnings.stream() + .map( + onField -> + new AddSingleElementAnnotation(onField, "SuppressWarnings", "NullAway", false)) + .collect(Collectors.toSet()); + result.addAll(suppressWarningsAnnotations); + + // Collect NullAway.Init SuppressWarnings + Set initializationSuppressWarningsAnnotations = + remainingFixes.stream() + .filter( + fix -> + fix.isOnField() + && (fix.reasons.contains("METHOD_NO_INIT") + || fix.reasons.contains("FIELD_NO_INIT"))) + // Filter nodes annotated with SuppressWarnings("NullAway") + .filter(fix -> !fieldsWithSuppressWarnings.contains(fix.toField())) + .map( + fix -> + new AddSingleElementAnnotation( + fix.toField(), "SuppressWarnings", "NullAway.Init", false)) + .collect(Collectors.toSet()); + result.addAll(initializationSuppressWarningsAnnotations); + injector.injectAnnotations(result); + // update log + context.log.updateInjectedAnnotations(result); + // Collect @NullUnmarked annotations on classes for any remaining error. + Utility.buildTarget(context); + remainingErrors = deserializeErrors(context.targetModuleInfo); + nullUnMarkedAnnotations = + remainingErrors.stream() + .filter(error -> !error.getRegion().isInAnonymousClass()) + .map( + error -> + new AddMarkerAnnotation( + context.targetModuleInfo.getLocationOnClass(error.getRegion().clazz), + config.nullUnMarkedAnnotation)) + .collect(Collectors.toSet()); + injector.injectAnnotations(nullUnMarkedAnnotations); + // update log + context.log.updateInjectedAnnotations(nullUnMarkedAnnotations); + } + + @Override + public NullAwayError createErrorFactory( + String errorType, + String errorMessage, + Region region, + int offset, + ImmutableSet resolvingFixes) { + return new NullAwayError(errorType, errorMessage, region, offset, resolvingFixes); + } + + @Override + public void verifyCheckerCompatibility(int version) { + Preconditions.checkArgument( + version == VERSION, + "This Annotator version only supports NullAway serialization version " + + VERSION + + ", but found: " + + version + + ", Please update Annotator or NullAway accordingly."); + } + + @Override + public void prepareConfigFilesForBuild(ModuleInfo moduleInfo) { + moduleInfo + .getModuleConfiguration() + .forEach( + module -> { + FixSerializationConfig.Builder nullAwayConfig = + new FixSerializationConfig.Builder() + .setSuggest(true, true) + .setOutputDirectory(module.dir.toString()) + .setFieldInitInfo(true); + nullAwayConfig.writeAsXML(module.checkerConfig.toString()); + }); + } +} diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAwayError.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAwayError.java new file mode 100644 index 000000000..09e0fb001 --- /dev/null +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAwayError.java @@ -0,0 +1,96 @@ +/* + * MIT License + * + * Copyright (c) 2022 Nima Karimipour + * + * 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.core.checkers.nullaway; + +import com.google.common.collect.ImmutableSet; +import edu.ucr.cs.riple.core.metadata.index.Error; +import edu.ucr.cs.riple.core.metadata.index.Fix; +import edu.ucr.cs.riple.core.metadata.trackers.Region; +import java.util.Objects; + +/** Represents an error reported by {@link NullAway}. */ +public class NullAwayError extends Error { + + /** Error type for method initialization errors from NullAway in {@code String}. */ + public static final String METHOD_INITIALIZER_ERROR = "METHOD_NO_INIT"; + /** Error type for field initialization errors from NullAway in {@code String}. */ + public static final String FIELD_INITIALIZER_ERROR = "FIELD_NO_INIT"; + + public NullAwayError( + String messageType, + String message, + Region region, + int offset, + ImmutableSet resolvingFixes) { + super(messageType, message, region, offset, resolvingFixes); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof NullAwayError)) { + return false; + } + NullAwayError error = (NullAwayError) o; + if (!messageType.equals(error.messageType)) { + return false; + } + if (!region.equals(error.region)) { + return false; + } + if (messageType.equals(METHOD_INITIALIZER_ERROR)) { + // we do not need to compare error messages as it can be the same error with a different error + // message and should not be treated as a separate error. + return true; + } + return message.equals(error.message) + && resolvingFixes.equals(error.resolvingFixes) + && offset == error.offset; + } + + @Override + public int hashCode() { + return Objects.hash( + messageType, + // to make sure equal objects will produce the same hashcode. + messageType.equals(METHOD_INITIALIZER_ERROR) ? METHOD_INITIALIZER_ERROR : message, + region, + resolvingFixes, + offset); + } + + /** + * Returns true if the error is an initialization error ({@code METHOD_NO_INIT} or {@code + * FIELD_NO_INIT}). + * + * @return true, if the error is an initialization error. + */ + public boolean isNonInitializationError() { + return !this.messageType.equals(METHOD_INITIALIZER_ERROR) + && !this.messageType.equals(FIELD_INITIALIZER_ERROR); + } +} diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/index/Error.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/index/Error.java index 48e6ca050..e1d4e2806 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/index/Error.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/index/Error.java @@ -46,9 +46,9 @@ public class Error { /** Error message. */ public final String message; /** The fixes which can resolve this error (possibly empty). */ - private final ImmutableSet resolvingFixes; + protected final ImmutableSet resolvingFixes; /** Offset of program point in original version where error is reported. */ - private final int offset; + protected final int offset; /** Containing region. */ protected final Region region; /** Error type for method initialization errors from NullAway in {@code String}. */ @@ -218,7 +218,8 @@ public String toString() { * @param errors Collection of errors. * @return Immutable set of fixes which can resolve all given errors. */ - public static ImmutableSet getResolvingFixesOfErrors(Collection errors) { + public static ImmutableSet getResolvingFixesOfErrors( + Collection errors) { // Each error has a set of resolving fixes and each fix has a set of reasons as why the fix has // been suggested. The final returned set of fixes should contain all the reasons it has been // suggested across the given collection. Map below stores all the set of reasons each fix is @@ -227,7 +228,7 @@ public static ImmutableSet getResolvingFixesOfErrors(Collection erro // Collect all reasons each fix is suggested across the given collection. Map> fixReasonsMap = new HashMap<>(); errors.stream() - .flatMap(error -> error.resolvingFixes.stream()) + .flatMap(error -> error.getResolvingFixes().stream()) .forEach( fix -> { if (fixReasonsMap.containsKey(fix)) { diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/index/Index.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/index/Index.java index 9b76aea60..e9e1c0423 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/index/Index.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/index/Index.java @@ -58,7 +58,7 @@ public Index(Context context, ModuleInfo moduleInfo) { /** Starts the reading and index process. */ public void index() { items.clear(); - Utility.readErrorsFromOutputDirectory(context, moduleInfo) + Utility.readErrorsFromOutputDirectory(context, moduleInfo, Error.class) .forEach(error -> items.put(error.getRegion(), error)); } diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/module/ModuleConfiguration.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/module/ModuleConfiguration.java index 41678247f..eb9a98cb8 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/module/ModuleConfiguration.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/module/ModuleConfiguration.java @@ -34,8 +34,8 @@ /** Container class to hold paths to nullaway and scanner config files. */ public class ModuleConfiguration { - /** Path to nullaway config. */ - public final Path nullawayConfig; + /** Path to checker config. */ + public final Path checkerConfig; /** Path to scanner config. */ public final Path scannerConfig; /** Directory where all serialized data from checkers are located. */ @@ -60,8 +60,8 @@ public static ModuleConfiguration buildFromJson(int id, Path globalDir, JSONObje id, globalDir, Paths.get(nullawayConfigPath), Paths.get(scannerConfigPath)); } - public ModuleConfiguration(int id, Path globalDir, Path nullawayConfig, Path scannerConfig) { - this.nullawayConfig = nullawayConfig; + public ModuleConfiguration(int id, Path globalDir, Path checkerConfig, Path scannerConfig) { + this.checkerConfig = checkerConfig; this.scannerConfig = scannerConfig; this.dir = globalDir.resolve(String.valueOf(id)); try { @@ -81,11 +81,11 @@ public boolean equals(Object o) { return false; } ModuleConfiguration that = (ModuleConfiguration) o; - return nullawayConfig.equals(that.nullawayConfig) && scannerConfig.equals(that.scannerConfig); + return checkerConfig.equals(that.checkerConfig) && scannerConfig.equals(that.scannerConfig); } @Override public int hashCode() { - return Objects.hash(nullawayConfig, scannerConfig); + return Objects.hash(checkerConfig, scannerConfig); } } diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/module/ModuleInfo.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/module/ModuleInfo.java index 25b395257..86d87b4ff 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/module/ModuleInfo.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/module/ModuleInfo.java @@ -46,6 +46,8 @@ public class ModuleInfo { /** The set of modules this moduleInfo is created for. */ private final ImmutableSet configurations; + private final Context context; + /** * This constructor is used to create a moduleInfo for a single module. * @@ -66,6 +68,7 @@ public ModuleInfo(Context context, ModuleConfiguration moduleConfiguration, Stri */ public ModuleInfo( Context context, ImmutableSet configurations, String buildCommand) { + this.context = context; // Build with scanner checker activated to generate required files to create the moduleInfo. Utility.setScannerCheckerActivation(context.config, configurations, true); configurations.forEach( @@ -75,7 +78,7 @@ public ModuleInfo( .setSuggest(true, true) .setOutputDirectory(module.dir.toString()) .setFieldInitInfo(true); - nullAwayConfig.writeAsXML(module.nullawayConfig.toString()); + nullAwayConfig.writeAsXML(module.checkerConfig.toString()); }); Utility.build(context, buildCommand); Utility.setScannerCheckerActivation(context.config, configurations, false); @@ -112,6 +115,24 @@ public NonnullStore getNonnullStore() { return nonnullStore; } + /** + * Getter for the module configuration this moduleInfo is created for. + * + * @return The module configuration this moduleInfo is created for. + */ + public ImmutableSet getModuleConfiguration() { + return configurations; + } + + /** + * Getter for the Annotator context. + * + * @return The Annotator context. + */ + public Context getContext() { + return context; + } + /** * Getter for the set of module configurations this moduleInfo is created for. * diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/util/Utility.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/util/Utility.java index 96d7a9487..a833f3617 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/util/Utility.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/util/Utility.java @@ -143,13 +143,12 @@ public static void writeReports(Context context, ImmutableSet reports) { * Reads serialized errors "errors.tsv" file in the output directory, and returns the collected * set of resolving fixes for read errors. * - * @param context Annotator context. Required to fetch the deserializer. - * @param moduleInfo ModuleInfo of the module which fixes are created for. + * @param context Context of the annotator. + * @param moduleInfo ModuleInfo of the module which errors are created for. * @return Set of collected fixes. */ public static Set readFixesFromOutputDirectory(Context context, ModuleInfo moduleInfo) { - Set errors = readErrorsFromOutputDirectory(context, moduleInfo); - return Error.getResolvingFixesOfErrors(errors); + return Error.getResolvingFixesOfErrors(context.checker.deserializeErrors(moduleInfo)); } /** @@ -159,8 +158,11 @@ public static Set readFixesFromOutputDirectory(Context context, ModuleInfo * @param moduleInfo ModuleInfo of the module which errors are created for. * @return Set of serialized errors. */ - public static Set readErrorsFromOutputDirectory(Context context, ModuleInfo moduleInfo) { - return context.deserializer.deserializeErrors(moduleInfo); + public static Set readErrorsFromOutputDirectory( + Context context, ModuleInfo moduleInfo, Class klass) { + return context.checker.deserializeErrors(moduleInfo).stream() + .map(klass::cast) + .collect(Collectors.toSet()); } /** @@ -271,7 +273,7 @@ public static void buildDownstreamDependencies(Context context) { .setSuggest(true, true) .setOutputDirectory(module.dir.toString()) .setFieldInitInfo(false); - nullAwayConfig.writeAsXML(module.nullawayConfig.toString()); + nullAwayConfig.writeAsXML(module.checkerConfig.toString()); }); build(context, context.config.downstreamDependenciesBuildCommand); } @@ -297,7 +299,7 @@ public static void buildTarget(Context context, boolean initSerializationEnabled .setSuggest(true, true) .setOutputDirectory(context.targetConfiguration.dir.toString()) .setFieldInitInfo(initSerializationEnabled); - nullAwayConfig.writeAsXML(context.targetConfiguration.nullawayConfig.toString()); + nullAwayConfig.writeAsXML(context.targetConfiguration.checkerConfig.toString()); build(context, context.config.buildCommand); } diff --git a/annotator-core/src/test/java/edu/ucr/cs/riple/core/ConfigurationTest.java b/annotator-core/src/test/java/edu/ucr/cs/riple/core/ConfigurationTest.java index f2813e8cf..2ece9a0a4 100644 --- a/annotator-core/src/test/java/edu/ucr/cs/riple/core/ConfigurationTest.java +++ b/annotator-core/src/test/java/edu/ucr/cs/riple/core/ConfigurationTest.java @@ -130,7 +130,7 @@ public void testRequiredFlagsCli() { assertEquals("./gradlew compileJava", config.buildCommand); assertEquals(testDir, config.globalDir); assertEquals("edu.ucr.Initializer", config.initializerAnnot); - assertEquals(Paths.get("0nullaway.xml"), config.target.nullawayConfig); + assertEquals(Paths.get("0nullaway.xml"), config.target.checkerConfig); assertEquals(Paths.get("0scanner.xml"), config.target.scannerConfig); }); } @@ -163,7 +163,7 @@ public void testRequiredFlagsForDownstreamDependencyAnalysisCli() { assertEquals("./gradlew compileJava", config.buildCommand); assertEquals(testDir, config.globalDir); assertEquals("edu.ucr.Initializer", config.initializerAnnot); - assertEquals(Paths.get("0nullaway.xml"), config.target.nullawayConfig); + assertEquals(Paths.get("0nullaway.xml"), config.target.checkerConfig); assertEquals(Paths.get("0scanner.xml"), config.target.scannerConfig); assertEquals(testDir.resolve("library-model.tsv"), config.nullawayLibraryModelLoaderPath); assertTrue(config.downStreamDependenciesAnalysisActivated); @@ -179,8 +179,7 @@ public void testRequiredFlagsForDownstreamDependencyAnalysisCli() { // downstream dependencies. ImmutableSet> actualDownstreamConfigPaths = config.downstreamConfigurations.stream() - .map( - moduleInfo -> new Pair<>(moduleInfo.nullawayConfig, moduleInfo.scannerConfig)) + .map(moduleInfo -> new Pair<>(moduleInfo.checkerConfig, moduleInfo.scannerConfig)) .collect(ImmutableSet.toImmutableSet()); assertEquals(actualDownstreamConfigPaths, expectedDownstreamConfigPaths); }); From 94bfbda2b6d2077b1ce164bbba21a576a66c5f5f Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Wed, 17 May 2023 10:00:55 -0700 Subject: [PATCH 20/35] fix test --- .../src/test/java/edu/ucr/cs/riple/core/tools/Utility.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/annotator-core/src/test/java/edu/ucr/cs/riple/core/tools/Utility.java b/annotator-core/src/test/java/edu/ucr/cs/riple/core/tools/Utility.java index 00c8e3d50..5da890c6d 100644 --- a/annotator-core/src/test/java/edu/ucr/cs/riple/core/tools/Utility.java +++ b/annotator-core/src/test/java/edu/ucr/cs/riple/core/tools/Utility.java @@ -147,7 +147,7 @@ public static void runTestWithMockedBuild(Path testDir, Runnable runnable) { try (MockedStatic utilMock = Mockito.mockStatic(edu.ucr.cs.riple.core.util.Utility.class, Mockito.CALLS_REAL_METHODS)) { utilMock - .when(() -> edu.ucr.cs.riple.core.util.Utility.buildTarget(Mockito.any())) + .when(() -> edu.ucr.cs.riple.core.util.Utility.build(Mockito.any(), Mockito.any())) .thenAnswer( invocation -> { Stream.of( From 07a6d3b04207180ff4a856996642653a6bc5a644 Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Wed, 17 May 2023 10:25:22 -0700 Subject: [PATCH 21/35] remove unused classes --- .../java/edu/ucr/cs/riple/core/Checker.java | 65 ------ .../java/edu/ucr/cs/riple/core/Config.java | 7 +- .../java/edu/ucr/cs/riple/core/Context.java | 3 +- .../io/deserializers/CheckerDeserializer.java | 59 ----- .../deserializers/DeserializerBaseClass.java | 103 -------- .../nullaway/NullAwayV3Deserializer.java | 219 ------------------ .../ucr/cs/riple/core/ConfigurationTest.java | 3 +- .../cs/riple/core/tools/CoreTestHelper.java | 4 +- 8 files changed, 8 insertions(+), 455 deletions(-) delete mode 100644 annotator-core/src/main/java/edu/ucr/cs/riple/core/Checker.java delete mode 100644 annotator-core/src/main/java/edu/ucr/cs/riple/core/io/deserializers/CheckerDeserializer.java delete mode 100644 annotator-core/src/main/java/edu/ucr/cs/riple/core/io/deserializers/DeserializerBaseClass.java delete mode 100644 annotator-core/src/main/java/edu/ucr/cs/riple/core/io/deserializers/nullaway/NullAwayV3Deserializer.java diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Checker.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Checker.java deleted file mode 100644 index 27ae1b1e7..000000000 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Checker.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2023 Nima Karimipour - * - * 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.core; - -import edu.ucr.cs.riple.core.io.deserializers.CheckerDeserializer; -import edu.ucr.cs.riple.core.io.deserializers.nullaway.NullAwayV3Deserializer; -import javax.annotation.Nullable; - -/** Enum class to represent the supported checkers. */ -public enum Checker { - NULLAWAY() { - @Override - public CheckerDeserializer getDeserializer(Context context) { - return new NullAwayV3Deserializer(context); - } - }; - - /** - * Returns the deserializer for the checker. - * - * @param context Annotator context. - * @return The corresponding deserializer. - */ - public abstract CheckerDeserializer getDeserializer(Context context); - - /** - * Returns the checker enum value corresponding to the given checker name. - * - * @param checkerName Name of the checker. - * @return Corresponding checker enum value. - */ - public static Checker getCheckerByName(@Nullable String checkerName) { - if (checkerName == null) { - throw new RuntimeException("Checker name cannot be null"); - } - for (Checker checker : Checker.values()) { - if (checker.name().equalsIgnoreCase(checkerName)) { - return checker; - } - } - throw new RuntimeException("No checker found for name: " + checkerName); - } -} diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Config.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Config.java index 1f24834a6..f5d39424e 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Config.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Config.java @@ -27,7 +27,6 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; -import edu.ucr.cs.riple.core.io.deserializers.CheckerDeserializer; import edu.ucr.cs.riple.core.module.ModuleConfiguration; import edu.ucr.cs.riple.core.util.Utility; import edu.ucr.cs.riple.scanner.generatedcode.SourceType; @@ -147,7 +146,7 @@ public class Config { * code and are generated by a processor. */ public final ImmutableSet generatedCodeDetectors; - /** Checker enum to retrieve checker specific instances. (e.g. {@link CheckerDeserializer}) */ + /** Checker enum to retrieve checker specific instances. */ public final String checker; /** @@ -649,7 +648,6 @@ public static class Builder { public String initializerAnnotation; public String nullableAnnotation; public String outputDir; - public Checker checker; /** * List of modules, did not use {@link java.util.Set} to preserve order. First project is the * target project. @@ -673,6 +671,7 @@ public static class Builder { public boolean useCacheImpact = false; public Set sourceTypes = new HashSet<>(); public int depth = 1; + public String checker; @SuppressWarnings("unchecked") public void write(Path path) { @@ -687,7 +686,7 @@ public void write(Path path) { nullableAnnotation, "Nullable Annotation must be initialized to construct the context."); JSONObject json = new JSONObject(); json.put("BUILD_COMMAND", buildCommand); - json.put("CHECKER", checker.name()); + json.put("CHECKER", checker); JSONObject annotation = new JSONObject(); annotation.put("INITIALIZER", initializerAnnotation); annotation.put("NULLABLE", nullableAnnotation); diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java index d35df2249..716a79096 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java @@ -27,7 +27,6 @@ import com.google.common.collect.ImmutableSet; import edu.ucr.cs.riple.core.checkers.Checker; import edu.ucr.cs.riple.core.checkers.CheckerBaseClass; -import edu.ucr.cs.riple.core.io.deserializers.CheckerDeserializer; import edu.ucr.cs.riple.core.log.Log; import edu.ucr.cs.riple.core.metadata.index.Error; import edu.ucr.cs.riple.core.module.ModuleConfiguration; @@ -61,7 +60,7 @@ public class Context { public final ModuleConfiguration targetConfiguration; /** Sets of context path information for all downstream dependencies. */ public final ImmutableSet downstreamConfigurations; - /** Checker enum to retrieve checker specific instances. (e.g. {@link CheckerDeserializer}) */ + /** Checker enum to retrieve checker specific instances. */ public final Checker checker; /** diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/io/deserializers/CheckerDeserializer.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/io/deserializers/CheckerDeserializer.java deleted file mode 100644 index c069af4ca..000000000 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/io/deserializers/CheckerDeserializer.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2022 Nima Karimipour - * - * 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.core.io.deserializers; - -import edu.ucr.cs.riple.core.Checker; -import edu.ucr.cs.riple.core.metadata.index.Error; -import edu.ucr.cs.riple.core.module.ModuleInfo; -import java.util.Set; - -/** - * Responsible for performing tasks related to NullAway / Type Annotator Scanner serialization - * features. - */ -public interface CheckerDeserializer { - - /** - * Deserialized errors reported by the checker on the passed modules. - * - * @param moduleInfo ModuleInfo of the module where errors are reported. - * @return Corresponding Error instance with the passed values. - */ - Set deserializeErrors(ModuleInfo moduleInfo); - - /** - * Returns the serialization version number which this adapter is associated with. - * - * @return Serialization number. - */ - int getVersionNumber(); - - /** - * Returns the name of the checker that this deserializer is associated with. - * - * @return Associated checker. - */ - Checker getAssociatedChecker(); -} diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/io/deserializers/DeserializerBaseClass.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/io/deserializers/DeserializerBaseClass.java deleted file mode 100644 index 1acf58e5c..000000000 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/io/deserializers/DeserializerBaseClass.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2022 Nima Karimipour - * - * 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.core.io.deserializers; - -import com.google.common.collect.ImmutableSet; -import edu.ucr.cs.riple.core.Checker; -import edu.ucr.cs.riple.core.Context; -import edu.ucr.cs.riple.core.metadata.index.Error; -import edu.ucr.cs.riple.core.metadata.index.Fix; -import edu.ucr.cs.riple.core.metadata.trackers.Region; -import edu.ucr.cs.riple.core.module.ModuleInfo; -import edu.ucr.cs.riple.injector.location.OnField; -import java.util.Set; - -/** Base class for all checker deserializers. */ -public abstract class DeserializerBaseClass implements CheckerDeserializer { - - /** Annotator context. */ - protected final Context context; - /** The checker that this deserializer is supporting. */ - protected final Checker checker; - /** The supporting serialization version of this deserializer. */ - protected final int version; - - public DeserializerBaseClass(Context context, Checker checker, int version) { - this.context = context; - this.checker = checker; - this.version = version; - } - - /** - * Creates an {@link Error} instance. - * - * @param errorType Error type. - * @param errorMessage Error message. - * @param region Region where the error is reported, - * @param offset offset of program point in original version where error is reported. - * @param resolvingFixes Set of fixes that resolve the error. - * @param moduleInfo ModuleInfo of the modules which errors are reported on. - * @return The corresponding error. - */ - protected Error createError( - String errorType, - String errorMessage, - Region region, - int offset, - ImmutableSet resolvingFixes, - ModuleInfo moduleInfo) { - ImmutableSet fixes = - resolvingFixes.stream() - .filter(f -> !moduleInfo.getNonnullStore().hasExplicitNonnullAnnotation(f.toLocation())) - .collect(ImmutableSet.toImmutableSet()); - return new Error(errorType, errorMessage, region, offset, fixes); - } - - /** - * Extends field variable names to full list to include all variables declared in the same - * statement. - * - * @param onField Location of the field. - * @return The updated given location. - */ - protected OnField extendVariableList(OnField onField, ModuleInfo moduleInfo) { - Set variables = - moduleInfo - .getFieldRegistry() - .getInLineMultipleFieldDeclarationsOnField(onField.clazz, onField.variables); - onField.variables.addAll(variables); - return onField; - } - - @Override - public int getVersionNumber() { - return version; - } - - @Override - public Checker getAssociatedChecker() { - return checker; - } -} diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/io/deserializers/nullaway/NullAwayV3Deserializer.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/io/deserializers/nullaway/NullAwayV3Deserializer.java deleted file mode 100644 index 342dc9f1e..000000000 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/io/deserializers/nullaway/NullAwayV3Deserializer.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2023 Nima Karimipour - * - * 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.core.io.deserializers.nullaway; - -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableSet; -import edu.ucr.cs.riple.core.Checker; -import edu.ucr.cs.riple.core.Context; -import edu.ucr.cs.riple.core.io.deserializers.DeserializerBaseClass; -import edu.ucr.cs.riple.core.metadata.index.Error; -import edu.ucr.cs.riple.core.metadata.index.Fix; -import edu.ucr.cs.riple.core.metadata.trackers.Region; -import edu.ucr.cs.riple.core.module.ModuleInfo; -import edu.ucr.cs.riple.injector.Helper; -import edu.ucr.cs.riple.injector.changes.AddMarkerAnnotation; -import edu.ucr.cs.riple.injector.location.Location; -import edu.ucr.cs.riple.injector.location.OnField; -import java.io.BufferedReader; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * NullAway serialization adapter for version 3. - * - *

Updates to previous version (version 1): - * - *

    - *
  • Serialized errors contain an extra column indicating the offset of the program point where - * the error is reported. - *
  • Serialized errors contain an extra column indicating the path to the containing source file - * where the error is reported - *
  • Type arguments and Type use annotations are excluded from the serialized method signatures. - *
- */ -public class NullAwayV3Deserializer extends DeserializerBaseClass { - - public NullAwayV3Deserializer(Context context) { - super(context, Checker.NULLAWAY, 3); - } - - @Override - public Set deserializeErrors(ModuleInfo moduleInfo) { - ImmutableSet paths = - moduleInfo.getModuleConfigurations().stream() - .map(config -> config.dir.resolve("errors.tsv")) - .collect(ImmutableSet.toImmutableSet()); - Set errors = new HashSet<>(); - paths.forEach( - path -> { - try { - try (BufferedReader br = Files.newBufferedReader(path, Charset.defaultCharset())) { - String line; - // Skip header. - br.readLine(); - while ((line = br.readLine()) != null) { - errors.add(deserializeErrorFromTSVLine(moduleInfo, line)); - } - } - } catch (IOException e) { - throw new RuntimeException("Exception happened in reading errors at: " + path, e); - } - }); - return errors; - } - - /** - * Deserializes an error from a TSV line. - * - * @param moduleInfo the moduleInfo of the module which the error is reported in. - * @param line Given TSV line. - * @return the deserialized error corresponding to the values in the given tsv line. - */ - private Error deserializeErrorFromTSVLine(ModuleInfo moduleInfo, String line) { - String[] values = line.split("\t"); - Preconditions.checkArgument( - values.length == 12, - "Expected 12 values to create Error instance in NullAway serialization version 2 but found: " - + values.length); - int offset = Integer.parseInt(values[4]); - Path path = Helper.deserializePath(values[5]); - String errorMessage = values[1]; - String errorType = values[0]; - Region region = new Region(values[2], values[3]); - Location nonnullTarget = - Location.createLocationFromArrayInfo(Arrays.copyOfRange(values, 6, 12)); - if (nonnullTarget == null && errorType.equals(Error.METHOD_INITIALIZER_ERROR)) { - ImmutableSet resolvingFixes = - generateFixesForUninitializedFields(errorMessage, region, moduleInfo).stream() - .filter( - fix -> - !moduleInfo.getNonnullStore().hasExplicitNonnullAnnotation(fix.toLocation())) - .collect(ImmutableSet.toImmutableSet()); - return createError( - errorType, - errorMessage, - region, - context.offsetHandler.getOriginalOffset(path, offset), - resolvingFixes, - moduleInfo); - } - if (nonnullTarget != null && nonnullTarget.isOnField()) { - nonnullTarget = extendVariableList(nonnullTarget.toField(), moduleInfo); - } - Fix resolvingFix = - nonnullTarget == null - ? null - : (moduleInfo.getNonnullStore().hasExplicitNonnullAnnotation(nonnullTarget) - // skip if element has explicit nonnull annotation. - ? null - : new Fix( - new AddMarkerAnnotation(nonnullTarget, context.config.nullableAnnot), - errorType, - true)); - return createError( - errorType, - errorMessage, - region, - context.offsetHandler.getOriginalOffset(path, offset), - resolvingFix == null ? ImmutableSet.of() : ImmutableSet.of(resolvingFix), - moduleInfo); - } - - /** - * Extracts uninitialized field names from the given error message. - * - * @param errorMessage Error message. - * @return Set of uninitialized field names. - */ - private Set extractUninitializedFieldNames(String errorMessage) { - String prefix = "initializer method does not guarantee @NonNull field"; - int begin = prefix.length(); - if (errorMessage.charAt(begin) == 's') { - begin += 1; - } - int end = errorMessage.indexOf(" is initialized along"); - end = end == -1 ? errorMessage.indexOf(" are initialized along ") : end; - if (end == -1) { - throw new RuntimeException( - "Error message for initializer error not recognized in version " - + getVersionNumber() - + ": " - + errorMessage); - } - String[] fieldsData = errorMessage.substring(begin, end).split(","); - Set fields = - Arrays.stream(fieldsData) - // NullAway serializes line number right after a field name starting with an open - // parentheses. (e.g. foo (line z)). This approach of extracting field names is - // extremely dependent on the format of NullAway error messages. Should be watched - // carefully and updated if the format is changed by NullAway (maybe regex?). - .map(s -> s.substring(0, s.indexOf("(")).trim()) - .collect(Collectors.toSet()); - if (fields.size() == 0) { - throw new RuntimeException( - "Could not extract any uninitialized field in message for initializer error in version " - + getVersionNumber() - + ": " - + errorMessage); - } - return fields; - } - - /** - * Generates a set of fixes for uninitialized fields from the given error message. - * - * @param errorMessage Given error message. - * @param region Region where the error is reported. - * @return Set of fixes for uninitialized fields to resolve the given error. - */ - protected ImmutableSet generateFixesForUninitializedFields( - String errorMessage, Region region, ModuleInfo moduleInfo) { - return extractUninitializedFieldNames(errorMessage).stream() - .map( - field -> { - OnField locationOnField = - moduleInfo.getFieldRegistry().getLocationOnField(region.clazz, field); - if (locationOnField == null) { - return null; - } - return new Fix( - new AddMarkerAnnotation( - extendVariableList(locationOnField, moduleInfo), - context.config.nullableAnnot), - Error.METHOD_INITIALIZER_ERROR, - true); - }) - .filter(Objects::nonNull) - .collect(ImmutableSet.toImmutableSet()); - } -} diff --git a/annotator-core/src/test/java/edu/ucr/cs/riple/core/ConfigurationTest.java b/annotator-core/src/test/java/edu/ucr/cs/riple/core/ConfigurationTest.java index 2ece9a0a4..6bc2d7aca 100644 --- a/annotator-core/src/test/java/edu/ucr/cs/riple/core/ConfigurationTest.java +++ b/annotator-core/src/test/java/edu/ucr/cs/riple/core/ConfigurationTest.java @@ -32,6 +32,7 @@ import com.github.javaparser.utils.Pair; import com.google.common.collect.ImmutableSet; +import edu.ucr.cs.riple.core.checkers.nullaway.NullAway; import edu.ucr.cs.riple.core.util.FixSerializationConfig; import edu.ucr.cs.riple.core.util.Utility; import edu.ucr.cs.riple.scanner.ScannerConfigWriter; @@ -98,7 +99,7 @@ public void init() { new CLIFlagWithValue("cp", testDir.resolve("paths.tsv")), new CLIFlagWithValue("i", "edu.ucr.Initializer"), new CLIFlagWithValue("d", testDir), - new CLIFlagWithValue("cn", Checker.NULLAWAY.name()))); + new CLIFlagWithValue("cn", NullAway.NAME))); requiredDownsStreamDependencyFlagsCli = new ArrayList<>( List.of( diff --git a/annotator-core/src/test/java/edu/ucr/cs/riple/core/tools/CoreTestHelper.java b/annotator-core/src/test/java/edu/ucr/cs/riple/core/tools/CoreTestHelper.java index 750985b99..5ff201c02 100644 --- a/annotator-core/src/test/java/edu/ucr/cs/riple/core/tools/CoreTestHelper.java +++ b/annotator-core/src/test/java/edu/ucr/cs/riple/core/tools/CoreTestHelper.java @@ -28,9 +28,9 @@ import edu.ucr.cs.riple.core.AnalysisMode; import edu.ucr.cs.riple.core.Annotator; -import edu.ucr.cs.riple.core.Checker; import edu.ucr.cs.riple.core.Config; import edu.ucr.cs.riple.core.Report; +import edu.ucr.cs.riple.core.checkers.nullaway.NullAway; import edu.ucr.cs.riple.core.log.Log; import edu.ucr.cs.riple.core.module.ModuleConfiguration; import edu.ucr.cs.riple.injector.Helper; @@ -384,7 +384,7 @@ public void makeAnnotatorConfigFile(Path configPath) { outDirPath.resolve(name + "-nullaway.xml"), outDirPath.resolve(name + "-scanner.xml"))) .collect(Collectors.toList()); - builder.checker = Checker.NULLAWAY; + builder.checker = NullAway.NAME; builder.nullableAnnotation = "javax.annotation.Nullable"; // In tests, we use NullAway @Initializer annotation. builder.initializerAnnotation = "com.uber.nullaway.annotations.Initializer"; From 0947c5d467cc6bde89d594ce327a3d7eb1a8b1d1 Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Wed, 17 May 2023 10:38:02 -0700 Subject: [PATCH 22/35] use prepare for build --- .../java/edu/ucr/cs/riple/core/Context.java | 2 +- .../ucr/cs/riple/core/checkers/Checker.java | 7 +++-- .../core/checkers/nullaway/NullAway.java | 23 +++++++-------- .../edu/ucr/cs/riple/core/util/Utility.java | 29 ++----------------- 4 files changed, 19 insertions(+), 42 deletions(-) diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java index 716a79096..1e606486f 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java @@ -60,7 +60,7 @@ public class Context { public final ModuleConfiguration targetConfiguration; /** Sets of context path information for all downstream dependencies. */ public final ImmutableSet downstreamConfigurations; - /** Checker enum to retrieve checker specific instances. */ + /** Checker instance. Used to execute checker specific tasks. */ public final Checker checker; /** diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java index 5f6bce13d..b1607dd3c 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java @@ -29,6 +29,7 @@ import edu.ucr.cs.riple.core.metadata.index.Error; import edu.ucr.cs.riple.core.metadata.index.Fix; import edu.ucr.cs.riple.core.metadata.trackers.Region; +import edu.ucr.cs.riple.core.module.ModuleConfiguration; import edu.ucr.cs.riple.core.module.ModuleInfo; import java.util.Set; @@ -50,7 +51,6 @@ public interface Checker { /** * Suppresses remaining errors reported by the checker. * - * @param context Annotator context. * @param injector Annotation injector to inject selected annotations. */ void suppressRemainingAnnotations(AnnotationInjector injector); @@ -83,7 +83,8 @@ T createErrorFactory( /** * Prepares the config files for the checker to run on the target module. * - * @param module Module where its config files should be prepared for a build. + * @param configurations Module configurations where their config files should be prepared for a + * build. */ - void prepareConfigFilesForBuild(ModuleInfo module); + void prepareConfigFilesForBuild(ImmutableSet configurations); } diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java index c824f6de8..316903ba5 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java @@ -31,6 +31,7 @@ import edu.ucr.cs.riple.core.injectors.AnnotationInjector; import edu.ucr.cs.riple.core.metadata.index.Fix; import edu.ucr.cs.riple.core.metadata.trackers.Region; +import edu.ucr.cs.riple.core.module.ModuleConfiguration; import edu.ucr.cs.riple.core.module.ModuleInfo; import edu.ucr.cs.riple.core.util.Utility; import edu.ucr.cs.riple.injector.Helper; @@ -379,17 +380,15 @@ public void verifyCheckerCompatibility(int version) { } @Override - public void prepareConfigFilesForBuild(ModuleInfo moduleInfo) { - moduleInfo - .getModuleConfiguration() - .forEach( - module -> { - FixSerializationConfig.Builder nullAwayConfig = - new FixSerializationConfig.Builder() - .setSuggest(true, true) - .setOutputDirectory(module.dir.toString()) - .setFieldInitInfo(true); - nullAwayConfig.writeAsXML(module.checkerConfig.toString()); - }); + public void prepareConfigFilesForBuild(ImmutableSet configurations) { + configurations.forEach( + module -> { + FixSerializationConfig.Builder nullAwayConfig = + new FixSerializationConfig.Builder() + .setSuggest(true, true) + .setOutputDirectory(module.dir.toString()) + .setFieldInitInfo(true); + nullAwayConfig.writeAsXML(module.checkerConfig.toString()); + }); } } diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/util/Utility.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/util/Utility.java index a833f3617..3b69a4bab 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/util/Utility.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/util/Utility.java @@ -266,40 +266,17 @@ public static TrackerNode deserializeTrackerNode(String[] values) { * @param context Annotator context. */ public static void buildDownstreamDependencies(Context context) { - context.downstreamConfigurations.forEach( - module -> { - FixSerializationConfig.Builder nullAwayConfig = - new FixSerializationConfig.Builder() - .setSuggest(true, true) - .setOutputDirectory(module.dir.toString()) - .setFieldInitInfo(false); - nullAwayConfig.writeAsXML(module.checkerConfig.toString()); - }); + context.checker.prepareConfigFilesForBuild(context.downstreamConfigurations); build(context, context.config.downstreamDependenciesBuildCommand); } - /** - * Builds target. - * - * @param context Annotator context. - */ - public static void buildTarget(Context context) { - buildTarget(context, false); - } - /** * Builds target with control on field initialization serialization. * * @param context Annotator context. - * @param initSerializationEnabled Activation flag for field initialization serialization. */ - public static void buildTarget(Context context, boolean initSerializationEnabled) { - FixSerializationConfig.Builder nullAwayConfig = - new FixSerializationConfig.Builder() - .setSuggest(true, true) - .setOutputDirectory(context.targetConfiguration.dir.toString()) - .setFieldInitInfo(initSerializationEnabled); - nullAwayConfig.writeAsXML(context.targetConfiguration.checkerConfig.toString()); + public static void buildTarget(Context context) { + context.checker.prepareConfigFilesForBuild(context.targetModuleInfo.getModuleConfigurations()); build(context, context.config.buildCommand); } From 42543a2588fbc2dfabdccaa7ffffad60f3fb92a2 Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Wed, 17 May 2023 10:47:58 -0700 Subject: [PATCH 23/35] delete write nullaway config as xml in utility --- .../nullaway/FixSerializationConfig.java | 97 ++++++++------- .../ucr/cs/riple/core/module/ModuleInfo.java | 2 +- .../core/util/FixSerializationConfig.java | 110 ------------------ .../edu/ucr/cs/riple/core/util/Utility.java | 60 ---------- .../ucr/cs/riple/core/ConfigurationTest.java | 5 +- 5 files changed, 51 insertions(+), 223 deletions(-) delete mode 100644 annotator-core/src/main/java/edu/ucr/cs/riple/core/util/FixSerializationConfig.java diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/FixSerializationConfig.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/FixSerializationConfig.java index fc0bd782d..a332d56f8 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/FixSerializationConfig.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/FixSerializationConfig.java @@ -72,6 +72,53 @@ public FixSerializationConfig( this.outputDirectory = outputDirectory; } + /** + * Writes the {@link FixSerializationConfig} in {@code XML} format. + * + * @param path Path to write the config at. + */ + public void writeNullAwayConfigInXMLFormat(String path) { + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + try { + DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + Document doc = docBuilder.newDocument(); + + // Root + Element rootElement = doc.createElement("serialization"); + doc.appendChild(rootElement); + + // Suggest + Element suggestElement = doc.createElement("suggest"); + suggestElement.setAttribute("active", String.valueOf(suggestEnabled)); + suggestElement.setAttribute("enclosing", String.valueOf(suggestEnclosing)); + rootElement.appendChild(suggestElement); + + // Field Initialization + Element fieldInitInfoEnabled = doc.createElement("fieldInitInfo"); + fieldInitInfoEnabled.setAttribute("active", String.valueOf(fieldInitInfoEnabled)); + rootElement.appendChild(fieldInitInfoEnabled); + + // Output dir + Element outputDir = doc.createElement("path"); + outputDir.setTextContent(outputDirectory); + rootElement.appendChild(outputDir); + + // UUID + Element uuid = doc.createElement("uuid"); + uuid.setTextContent(UUID.randomUUID().toString()); + rootElement.appendChild(uuid); + + // Writings + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + DOMSource source = new DOMSource(doc); + StreamResult result = new StreamResult(new File(path)); + transformer.transform(source, result); + } catch (ParserConfigurationException | TransformerException e) { + throw new RuntimeException("Error happened in writing config.", e); + } + } + /** Builder class for Serialization Config */ public static class Builder { @@ -109,7 +156,7 @@ public Builder setOutputDirectory(String outputDir) { */ public void writeAsXML(String path) { FixSerializationConfig config = this.build(); - writeNullAwayConfigInXMLFormat(config, path); + config.writeNullAwayConfigInXMLFormat(path); } public FixSerializationConfig build() { @@ -118,53 +165,5 @@ public FixSerializationConfig build() { } return new FixSerializationConfig(suggestEnabled, suggestEnclosing, fieldInitInfo, outputDir); } - - /** - * Writes the {@link FixSerializationConfig} in {@code XML} format. - * - * @param config Config file to write. - * @param path Path to write the config at. - */ - public static void writeNullAwayConfigInXMLFormat(FixSerializationConfig config, String path) { - DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); - try { - DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); - Document doc = docBuilder.newDocument(); - - // Root - Element rootElement = doc.createElement("serialization"); - doc.appendChild(rootElement); - - // Suggest - Element suggestElement = doc.createElement("suggest"); - suggestElement.setAttribute("active", String.valueOf(config.suggestEnabled)); - suggestElement.setAttribute("enclosing", String.valueOf(config.suggestEnclosing)); - rootElement.appendChild(suggestElement); - - // Field Initialization - Element fieldInitInfoEnabled = doc.createElement("fieldInitInfo"); - fieldInitInfoEnabled.setAttribute("active", String.valueOf(config.fieldInitInfoEnabled)); - rootElement.appendChild(fieldInitInfoEnabled); - - // Output dir - Element outputDir = doc.createElement("path"); - outputDir.setTextContent(config.outputDirectory); - rootElement.appendChild(outputDir); - - // UUID - Element uuid = doc.createElement("uuid"); - uuid.setTextContent(UUID.randomUUID().toString()); - rootElement.appendChild(uuid); - - // Writings - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - Transformer transformer = transformerFactory.newTransformer(); - DOMSource source = new DOMSource(doc); - StreamResult result = new StreamResult(new File(path)); - transformer.transform(source, result); - } catch (ParserConfigurationException | TransformerException e) { - throw new RuntimeException("Error happened in writing config.", e); - } - } } } diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/module/ModuleInfo.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/module/ModuleInfo.java index 86d87b4ff..8cf0a4485 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/module/ModuleInfo.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/module/ModuleInfo.java @@ -26,10 +26,10 @@ import com.google.common.collect.ImmutableSet; import edu.ucr.cs.riple.core.Context; +import edu.ucr.cs.riple.core.checkers.nullaway.FixSerializationConfig; import edu.ucr.cs.riple.core.metadata.field.FieldRegistry; import edu.ucr.cs.riple.core.metadata.index.NonnullStore; import edu.ucr.cs.riple.core.metadata.method.MethodRegistry; -import edu.ucr.cs.riple.core.util.FixSerializationConfig; import edu.ucr.cs.riple.core.util.Utility; import edu.ucr.cs.riple.injector.location.Location; import edu.ucr.cs.riple.injector.location.OnClass; diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/util/FixSerializationConfig.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/util/FixSerializationConfig.java deleted file mode 100644 index 48a6fec5d..000000000 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/util/FixSerializationConfig.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2022 Uber Technologies, Inc. - * - * 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.core.util; - -import javax.annotation.Nullable; - -/** - * IMPORTANT NOTE: THIS CLASS IS COPIED FROM NULLAWAY, WE COPIED THE CLASS CONTENT HERE TO REMOVE - * DEPENDENCY TO NULLAWAY. - * - *

Context class for Fix Serialization package. - */ -public class FixSerializationConfig { - - public final boolean suggestEnabled; - - public final boolean suggestEnclosing; - - public final boolean fieldInitInfoEnabled; - - /** The directory where all files generated/read by Fix Serialization package resides. */ - @Nullable public final String outputDirectory; - - /** Default Constructor, all features are disabled with this config. */ - public FixSerializationConfig() { - suggestEnabled = false; - suggestEnclosing = false; - fieldInitInfoEnabled = false; - outputDirectory = null; - } - - public FixSerializationConfig( - boolean suggestEnabled, - boolean suggestEnclosing, - boolean fieldInitInfoEnabled, - String outputDirectory) { - this.suggestEnabled = suggestEnabled; - this.suggestEnclosing = suggestEnclosing; - this.fieldInitInfoEnabled = fieldInitInfoEnabled; - this.outputDirectory = outputDirectory; - } - - /** Builder class for Serialization Context */ - public static class Builder { - - private boolean suggestEnabled; - private boolean suggestEnclosing; - private boolean fieldInitInfo; - @Nullable private String outputDir; - - public Builder() { - suggestEnabled = false; - suggestEnclosing = false; - fieldInitInfo = false; - } - - public Builder setSuggest(boolean value, boolean withEnclosing) { - this.suggestEnabled = value; - this.suggestEnclosing = withEnclosing && suggestEnabled; - return this; - } - - public Builder setFieldInitInfo(boolean enabled) { - this.fieldInitInfo = enabled; - return this; - } - - public Builder setOutputDirectory(String outputDir) { - this.outputDir = outputDir; - return this; - } - - /** - * Builds and writes the context with the state in builder at the given path as XML. - * - * @param path path to write the context file. - */ - public void writeAsXML(String path) { - FixSerializationConfig config = this.build(); - Utility.writeNullAwayConfigInXMLFormat(config, path); - } - - public FixSerializationConfig build() { - if (outputDir == null) { - throw new IllegalStateException("did not set mandatory output directory"); - } - return new FixSerializationConfig(suggestEnabled, suggestEnclosing, fieldInitInfo, outputDir); - } - } -} diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/util/Utility.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/util/Utility.java index 3b69a4bab..4ce510864 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/util/Utility.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/util/Utility.java @@ -40,7 +40,6 @@ import edu.ucr.cs.riple.scanner.generatedcode.SourceType; import java.io.BufferedReader; import java.io.BufferedWriter; -import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; @@ -51,23 +50,12 @@ import java.util.Collections; import java.util.List; import java.util.Set; -import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; import me.tongfei.progressbar.ProgressBar; import me.tongfei.progressbar.ProgressBarStyle; import org.json.simple.JSONArray; import org.json.simple.JSONObject; -import org.w3c.dom.Document; -import org.w3c.dom.Element; /** Utility class. */ public class Utility { @@ -165,54 +153,6 @@ public static Set readErrorsFromOutputDirectory( .collect(Collectors.toSet()); } - /** - * Writes the {@link FixSerializationConfig} in {@code XML} format. - * - * @param config Context file to write. - * @param path Path to write the context at. - */ - public static void writeNullAwayConfigInXMLFormat(FixSerializationConfig config, String path) { - DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); - try { - DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); - Document doc = docBuilder.newDocument(); - - // Root - Element rootElement = doc.createElement("serialization"); - doc.appendChild(rootElement); - - // Suggest - Element suggestElement = doc.createElement("suggest"); - suggestElement.setAttribute("active", String.valueOf(config.suggestEnabled)); - suggestElement.setAttribute("enclosing", String.valueOf(config.suggestEnclosing)); - rootElement.appendChild(suggestElement); - - // Field Initialization - Element fieldInitInfoEnabled = doc.createElement("fieldInitInfo"); - fieldInitInfoEnabled.setAttribute("active", String.valueOf(config.fieldInitInfoEnabled)); - rootElement.appendChild(fieldInitInfoEnabled); - - // Output dir - Element outputDir = doc.createElement("path"); - outputDir.setTextContent(config.outputDirectory); - rootElement.appendChild(outputDir); - - // UUID - Element uuid = doc.createElement("uuid"); - uuid.setTextContent(UUID.randomUUID().toString()); - rootElement.appendChild(uuid); - - // Writings - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - Transformer transformer = transformerFactory.newTransformer(); - DOMSource source = new DOMSource(doc); - StreamResult result = new StreamResult(new File(path)); - transformer.transform(source, result); - } catch (ParserConfigurationException | TransformerException e) { - throw new RuntimeException("Error happened in writing config.", e); - } - } - /** * Activates/Deactivates {@link AnnotatorScanner} features by updating the {@link * edu.ucr.cs.riple.scanner.Config} in {@code XML} format for the given modules. diff --git a/annotator-core/src/test/java/edu/ucr/cs/riple/core/ConfigurationTest.java b/annotator-core/src/test/java/edu/ucr/cs/riple/core/ConfigurationTest.java index 6bc2d7aca..50536852c 100644 --- a/annotator-core/src/test/java/edu/ucr/cs/riple/core/ConfigurationTest.java +++ b/annotator-core/src/test/java/edu/ucr/cs/riple/core/ConfigurationTest.java @@ -32,9 +32,8 @@ import com.github.javaparser.utils.Pair; import com.google.common.collect.ImmutableSet; +import edu.ucr.cs.riple.core.checkers.nullaway.FixSerializationConfig; import edu.ucr.cs.riple.core.checkers.nullaway.NullAway; -import edu.ucr.cs.riple.core.util.FixSerializationConfig; -import edu.ucr.cs.riple.core.util.Utility; import edu.ucr.cs.riple.scanner.ScannerConfigWriter; import java.io.FileOutputStream; import java.io.IOException; @@ -193,7 +192,7 @@ public void testConfigFilesHaveDifferentUUID() { FixSerializationConfig config = new FixSerializationConfig(); Path nullawayConfigPath = testDir.resolve("nullaway.xml"); for (int i = 0; i < 5; i++) { - Utility.writeNullAwayConfigInXMLFormat(config, nullawayConfigPath.toString()); + config.writeNullAwayConfigInXMLFormat(nullawayConfigPath.toString()); String uuid = getValueFromTag(nullawayConfigPath, "/serialization/uuid"); if (observed.contains(uuid)) { throw new IllegalStateException( From f2e2a1e2d158725cf69949068809889b71922a64 Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Wed, 17 May 2023 11:02:27 -0700 Subject: [PATCH 24/35] fix bug --- .../checkers/nullaway/FixSerializationConfig.java | 2 +- .../edu/ucr/cs/riple/core/module/ModuleInfo.java | 13 ++----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/FixSerializationConfig.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/FixSerializationConfig.java index a332d56f8..287d74e5a 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/FixSerializationConfig.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/FixSerializationConfig.java @@ -95,7 +95,7 @@ public void writeNullAwayConfigInXMLFormat(String path) { // Field Initialization Element fieldInitInfoEnabled = doc.createElement("fieldInitInfo"); - fieldInitInfoEnabled.setAttribute("active", String.valueOf(fieldInitInfoEnabled)); + fieldInitInfoEnabled.setAttribute("active", String.valueOf(this.fieldInitInfoEnabled)); rootElement.appendChild(fieldInitInfoEnabled); // Output dir diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/module/ModuleInfo.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/module/ModuleInfo.java index 8cf0a4485..ebf61fe19 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/module/ModuleInfo.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/module/ModuleInfo.java @@ -26,7 +26,6 @@ import com.google.common.collect.ImmutableSet; import edu.ucr.cs.riple.core.Context; -import edu.ucr.cs.riple.core.checkers.nullaway.FixSerializationConfig; import edu.ucr.cs.riple.core.metadata.field.FieldRegistry; import edu.ucr.cs.riple.core.metadata.index.NonnullStore; import edu.ucr.cs.riple.core.metadata.method.MethodRegistry; @@ -69,20 +68,12 @@ public ModuleInfo(Context context, ModuleConfiguration moduleConfiguration, Stri public ModuleInfo( Context context, ImmutableSet configurations, String buildCommand) { this.context = context; + this.configurations = configurations; // Build with scanner checker activated to generate required files to create the moduleInfo. Utility.setScannerCheckerActivation(context.config, configurations, true); - configurations.forEach( - module -> { - FixSerializationConfig.Builder nullAwayConfig = - new FixSerializationConfig.Builder() - .setSuggest(true, true) - .setOutputDirectory(module.dir.toString()) - .setFieldInitInfo(true); - nullAwayConfig.writeAsXML(module.checkerConfig.toString()); - }); + context.checker.prepareConfigFilesForBuild(configurations); Utility.build(context, buildCommand); Utility.setScannerCheckerActivation(context.config, configurations, false); - this.configurations = configurations; this.nonnullStore = new NonnullStore(configurations); this.fieldRegistry = new FieldRegistry(configurations); this.methodRegistry = new MethodRegistry(context); From 9c56a43287151cfa883e8ca1591cbbb198a77d6a Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Wed, 17 May 2023 11:23:47 -0700 Subject: [PATCH 25/35] rename checker name --- .../src/main/java/edu/ucr/cs/riple/core/Config.java | 10 ++++++---- .../src/main/java/edu/ucr/cs/riple/core/Context.java | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Config.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Config.java index f5d39424e..8e1b96366 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Config.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Config.java @@ -146,8 +146,10 @@ public class Config { * code and are generated by a processor. */ public final ImmutableSet generatedCodeDetectors; - /** Checker enum to retrieve checker specific instances. */ - public final String checker; + /** + * Checker name to retrieve the {@link edu.ucr.cs.riple.core.checkers.Checker} specific instance. + */ + public final String checkerName; /** * Builds context from command line arguments. @@ -380,7 +382,7 @@ public Config(String[] args) { cmd.hasOption(nullableOption.getLongOpt()) ? cmd.getOptionValue(nullableOption.getLongOpt()) : "javax.annotation.Nullable"; - this.checker = cmd.getOptionValue(checkerNameOption); + this.checkerName = cmd.getOptionValue(checkerNameOption); this.initializerAnnot = cmd.getOptionValue(initializerOption.getLongOpt()); this.depth = Integer.parseInt( @@ -466,7 +468,7 @@ public Config(Path configPath) { } catch (Exception e) { throw new RuntimeException("Error in reading/parsing context at path: " + configPath, e); } - this.checker = getValueFromKey(jsonObject, "CHECKER", String.class).orElse(null); + this.checkerName = getValueFromKey(jsonObject, "CHECKER", String.class).orElse(null); this.depth = getValueFromKey(jsonObject, "DEPTH", Long.class).orElse((long) 1).intValue(); this.chain = getValueFromKey(jsonObject, "CHAIN", Boolean.class).orElse(false); this.redirectBuildOutputToStdErr = diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java index 1e606486f..2fc5cf216 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java @@ -70,7 +70,7 @@ public class Context { */ public Context(Config config) { this.config = config; - this.checker = CheckerBaseClass.getCheckerByName(config.checker, this); + this.checker = CheckerBaseClass.getCheckerByName(config.checkerName, this); this.offsetHandler = new OffsetHandler(); this.downstreamConfigurations = config.downstreamConfigurations; this.log = new Log(); From 8c384126fbe0b2e24f04b10a13995636c753ef93 Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Wed, 17 May 2023 11:40:31 -0700 Subject: [PATCH 26/35] update --- .../java/edu/ucr/cs/riple/core/Annotator.java | 16 +------------- .../ucr/cs/riple/core/checkers/Checker.java | 8 +++++++ .../core/checkers/nullaway/NullAway.java | 22 +++++++++++++++++++ 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Annotator.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Annotator.java index 9bcb4f262..41dbd8541 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Annotator.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Annotator.java @@ -37,12 +37,8 @@ import edu.ucr.cs.riple.core.evaluators.suppliers.TargetModuleSupplier; import edu.ucr.cs.riple.core.injectors.AnnotationInjector; import edu.ucr.cs.riple.core.injectors.PhysicalInjector; -import edu.ucr.cs.riple.core.metadata.field.FieldInitializationStore; import edu.ucr.cs.riple.core.metadata.index.Fix; import edu.ucr.cs.riple.core.util.Utility; -import edu.ucr.cs.riple.injector.changes.AddAnnotation; -import edu.ucr.cs.riple.injector.changes.AddMarkerAnnotation; -import edu.ucr.cs.riple.injector.location.OnField; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -90,17 +86,7 @@ public void start() { */ private void preprocess() { System.out.println("Preprocessing..."); - Set uninitializedFields = - Utility.readFixesFromOutputDirectory(context, context.targetModuleInfo).stream() - .filter(fix -> fix.isOnField() && fix.reasons.contains("FIELD_NO_INIT")) - .map(Fix::toField) - .collect(Collectors.toSet()); - FieldInitializationStore fieldInitializationStore = new FieldInitializationStore(context); - Set initializers = - fieldInitializationStore.findInitializers(uninitializedFields).stream() - .map(onMethod -> new AddMarkerAnnotation(onMethod, config.initializerAnnot)) - .collect(Collectors.toSet()); - this.injector.injectAnnotations(initializers); + context.checker.preprocess(injector); } /** Performs iterations of inference/injection until no unseen fix is suggested. */ diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java index b1607dd3c..ecd3521d7 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java @@ -55,6 +55,14 @@ public interface Checker { */ void suppressRemainingAnnotations(AnnotationInjector injector); + /** + * Used to do any pre-processing steps before running the inference. + * + * @param injector Annotation injector, can be used to inject any annotations during the + * pre-processing phase. + */ + void preprocess(AnnotationInjector injector); + /** * Creates an {@link Error} instance from the given parameters. * diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java index 316903ba5..7dec24fc2 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java @@ -29,6 +29,7 @@ import edu.ucr.cs.riple.core.Context; import edu.ucr.cs.riple.core.checkers.CheckerBaseClass; import edu.ucr.cs.riple.core.injectors.AnnotationInjector; +import edu.ucr.cs.riple.core.metadata.field.FieldInitializationStore; import edu.ucr.cs.riple.core.metadata.index.Fix; import edu.ucr.cs.riple.core.metadata.trackers.Region; import edu.ucr.cs.riple.core.module.ModuleConfiguration; @@ -358,6 +359,27 @@ public void suppressRemainingAnnotations(AnnotationInjector injector) { context.log.updateInjectedAnnotations(nullUnMarkedAnnotations); } + @Override + public void preprocess(AnnotationInjector injector) { + // Collect @Initializer annotations. Heuristically, we add @Initializer on methods which writes + // a @Nonnull value to more than one uninitialized field, and guarantees initialized fields are + // nonnull at all exit paths. + // Collect uninitialized fields. + Set uninitializedFields = + Utility.readFixesFromOutputDirectory(context, context.targetModuleInfo).stream() + .filter(fix -> fix.isOnField() && fix.reasons.contains("FIELD_NO_INIT")) + .map(Fix::toField) + .collect(Collectors.toSet()); + FieldInitializationStore fieldInitializationStore = new FieldInitializationStore(context); + // Collect selected initializers methods. + Set initializers = + fieldInitializationStore.findInitializers(uninitializedFields).stream() + .map(onMethod -> new AddMarkerAnnotation(onMethod, config.initializerAnnot)) + .collect(Collectors.toSet()); + // Inject @Initializer annotations. + injector.injectAnnotations(initializers); + } + @Override public NullAwayError createErrorFactory( String errorType, From c2da72f3ed242deea7b243d9388230bc387696df Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Wed, 17 May 2023 13:01:56 -0700 Subject: [PATCH 27/35] fix bug --- .../java/edu/ucr/cs/riple/core/Context.java | 4 ++- .../ucr/cs/riple/core/checkers/Checker.java | 4 +-- .../riple/core/checkers/CheckerBaseClass.java | 7 ++--- .../core/checkers/nullaway/NullAway.java | 30 +++++++++++++------ 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java index 2fc5cf216..5044e5a06 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Context.java @@ -70,12 +70,14 @@ public class Context { */ public Context(Config config) { this.config = config; - this.checker = CheckerBaseClass.getCheckerByName(config.checkerName, this); this.offsetHandler = new OffsetHandler(); this.downstreamConfigurations = config.downstreamConfigurations; this.log = new Log(); this.targetConfiguration = config.target; + this.checker = CheckerBaseClass.getCheckerByName(config.checkerName, this); this.targetModuleInfo = new ModuleInfo(this, config.target, config.buildCommand); + // Checker compatibility check must be after target module info is initialized. + this.checker.verifyCheckerCompatibility(); } /** Responsible for handling offset changes in source file. */ diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java index ecd3521d7..49e1db265 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java @@ -83,10 +83,8 @@ T createErrorFactory( /** * Verifies that the checker representation in Annotator is compatible with the actual running * checker on the target module. - * - * @param version The version of the actual running checker. */ - void verifyCheckerCompatibility(int version); + void verifyCheckerCompatibility(); /** * Prepares the config files for the checker to run on the target module. diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java index ab25760df..3464fafe7 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java @@ -40,15 +40,12 @@ public abstract class CheckerBaseClass implements Checker { /** Annotator config. */ protected final Config config; - /** The supporting serialization version of this deserializer. */ - protected final int version; /** Annotator context. */ protected final Context context; - public CheckerBaseClass(Context context, int version) { + public CheckerBaseClass(Context context) { this.context = context; this.config = context.config; - this.version = version; } /** @@ -96,7 +93,7 @@ protected static OnField extendVariableList(OnField onField, ModuleInfo moduleIn * Returns the checker instance by its name. * * @param name name of the checker. - * @param config annotator config. + * @param context annotator context. * @return the checker instance with the given name. */ public static Checker getCheckerByName(String name, Context context) { diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java index 7dec24fc2..4ed340653 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java @@ -64,7 +64,7 @@ public class NullAway extends CheckerBaseClass { public static final int VERSION = 3; public NullAway(Context context) { - super(context, 3); + super(context); } @Override @@ -391,14 +391,26 @@ public NullAwayError createErrorFactory( } @Override - public void verifyCheckerCompatibility(int version) { - Preconditions.checkArgument( - version == VERSION, - "This Annotator version only supports NullAway serialization version " - + VERSION - + ", but found: " - + version - + ", Please update Annotator or NullAway accordingly."); + public void verifyCheckerCompatibility() { + Path pathToSerializationVersion = + config.globalDir.resolve("0").resolve("serialization_version.txt"); + if (!Files.exists(pathToSerializationVersion)) { + throw new RuntimeException( + "This version of Annotator does not support the using NullAway version, please upgrade NullAway to version >= 0.10.10"); + } + try { + int version = + Integer.parseInt(Files.readString(pathToSerializationVersion, Charset.defaultCharset())); + Preconditions.checkArgument( + version == VERSION, + "This Annotator version only supports NullAway serialization version " + + VERSION + + ", but found: " + + version + + ", Please update Annotator or NullAway accordingly."); + } catch (IOException e) { + throw new RuntimeException(e); + } } @Override From 155aaca39008837de7c4227f486dee707432032c Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Wed, 17 May 2023 13:12:38 -0700 Subject: [PATCH 28/35] fix up interface names --- .../ucr/cs/riple/core/checkers/Checker.java | 6 ++-- .../riple/core/checkers/CheckerBaseClass.java | 28 ------------------- .../core/checkers/nullaway/NullAway.java | 27 +++++++++--------- .../core/checkers/nullaway/NullAwayError.java | 2 +- 4 files changed, 18 insertions(+), 45 deletions(-) diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java index 49e1db265..630b9f580 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java @@ -71,14 +71,16 @@ public interface Checker { * @param region Region where the error is reported, * @param offset offset of program point in original version where error is reported. * @param resolvingFixes Set of fixes that resolve the error. + * @param module Module where the error is reported. * @return The corresponding error. */ - T createErrorFactory( + T createError( String errorType, String errorMessage, Region region, int offset, - ImmutableSet resolvingFixes); + ImmutableSet resolvingFixes, + ModuleInfo module); /** * Verifies that the checker representation in Annotator is compatible with the actual running diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java index 3464fafe7..971d0d44b 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java @@ -24,13 +24,10 @@ package edu.ucr.cs.riple.core.checkers; -import com.google.common.collect.ImmutableSet; import edu.ucr.cs.riple.core.Config; import edu.ucr.cs.riple.core.Context; import edu.ucr.cs.riple.core.checkers.nullaway.NullAway; import edu.ucr.cs.riple.core.metadata.index.Error; -import edu.ucr.cs.riple.core.metadata.index.Fix; -import edu.ucr.cs.riple.core.metadata.trackers.Region; import edu.ucr.cs.riple.core.module.ModuleInfo; import edu.ucr.cs.riple.injector.location.OnField; import java.util.Set; @@ -48,31 +45,6 @@ public CheckerBaseClass(Context context) { this.config = context.config; } - /** - * Creates an {@link Error} instance. - * - * @param errorType Error type. - * @param errorMessage Error message. - * @param region Region where the error is reported, - * @param offset offset of program point in original version where error is reported. - * @param resolvingFixes Set of fixes that resolve the error. - * @param module Module where the error is reported. - * @return The corresponding error. - */ - public T createError( - String errorType, - String errorMessage, - Region region, - int offset, - ImmutableSet resolvingFixes, - ModuleInfo module) { - ImmutableSet fixes = - resolvingFixes.stream() - .filter(f -> !module.getNonnullStore().hasExplicitNonnullAnnotation(f.toLocation())) - .collect(ImmutableSet.toImmutableSet()); - return createErrorFactory(errorType, errorMessage, region, offset, fixes); - } - /** * Extends field variable names to full list to include all variables declared in the same * statement. diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java index 4ed340653..2246c5330 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022 Nima Karimipour + * Copyright (c) 2023 Nima Karimipour * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -115,11 +115,7 @@ private NullAwayError deserializeErrorFromTSVLine(ModuleInfo moduleInfo, String Location.createLocationFromArrayInfo(Arrays.copyOfRange(values, 6, 12)); if (nonnullTarget == null && errorType.equals(NullAwayError.METHOD_INITIALIZER_ERROR)) { ImmutableSet resolvingFixes = - generateFixesForUninitializedFields(errorMessage, region, moduleInfo).stream() - .filter( - fix -> - !moduleInfo.getNonnullStore().hasExplicitNonnullAnnotation(fix.toLocation())) - .collect(ImmutableSet.toImmutableSet()); + generateFixesForUninitializedFields(errorMessage, region, moduleInfo); return createError( errorType, errorMessage, @@ -134,11 +130,8 @@ private NullAwayError deserializeErrorFromTSVLine(ModuleInfo moduleInfo, String Fix resolvingFix = nonnullTarget == null ? null - : (moduleInfo.getNonnullStore().hasExplicitNonnullAnnotation(nonnullTarget) - // skip if element has explicit nonnull annotation. - ? null - : new Fix( - new AddMarkerAnnotation(nonnullTarget, config.nullableAnnot), errorType, true)); + : new Fix( + new AddMarkerAnnotation(nonnullTarget, config.nullableAnnot), errorType, true); return createError( errorType, errorMessage, @@ -381,13 +374,19 @@ public void preprocess(AnnotationInjector injector) { } @Override - public NullAwayError createErrorFactory( + public NullAwayError createError( String errorType, String errorMessage, Region region, int offset, - ImmutableSet resolvingFixes) { - return new NullAwayError(errorType, errorMessage, region, offset, resolvingFixes); + ImmutableSet resolvingFixes, + ModuleInfo module) { + // Filter fixes on elements with explicit nonnull annotations. + ImmutableSet cleanedResolvingFixes = + resolvingFixes.stream() + .filter(f -> !module.getNonnullStore().hasExplicitNonnullAnnotation(f.toLocation())) + .collect(ImmutableSet.toImmutableSet()); + return new NullAwayError(errorType, errorMessage, region, offset, cleanedResolvingFixes); } @Override diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAwayError.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAwayError.java index 09e0fb001..126916ba1 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAwayError.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAwayError.java @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022 Nima Karimipour + * Copyright (c) 2023 Nima Karimipour * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal From 37b09fd7a9b2c0badc2619d845a031ff44559fe5 Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Wed, 17 May 2023 16:18:58 -0700 Subject: [PATCH 29/35] initial --- .../core/checkers/ucrtaint/UCRTaint.java | 133 ++++++++++++++++++ .../core/checkers/ucrtaint/UCRTaintError.java | 43 ++++++ 2 files changed, 176 insertions(+) create mode 100644 annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaint.java create mode 100644 annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaintError.java diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaint.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaint.java new file mode 100644 index 000000000..186d1f03b --- /dev/null +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaint.java @@ -0,0 +1,133 @@ +* + * MIT License + * + * Copyright (c) 2023 Nima Karimipour + * + * 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.core.checkers.ucrtaint; + +import com.google.common.collect.ImmutableSet; +import edu.ucr.cs.riple.core.Config; +import edu.ucr.cs.riple.core.Context; +import edu.ucr.cs.riple.core.checkers.CheckerBaseClass; +import edu.ucr.cs.riple.core.checkers.ucrtaint.UCRTaintError; +import edu.ucr.cs.riple.core.injectors.AnnotationInjector; +import edu.ucr.cs.riple.core.metadata.index.Fix; +import edu.ucr.cs.riple.core.metadata.trackers.Region; +import edu.ucr.cs.riple.core.module.ModuleConfiguration; +import edu.ucr.cs.riple.core.module.ModuleInfo; +import edu.ucr.cs.riple.injector.changes.AddTypeUseMarkerAnnotation; +import edu.ucr.cs.riple.injector.location.Location; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Set; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +/** + * Represents UCRTaint checker in + * Annotator. + */ +public class UCRTaint extends CheckerBaseClass { + + /** The name of the checker. This is used to identify the checker in the configuration file. */ + public static final String NAME = "UCRTaint"; + + public UCRTaint(Context context) { + super(context); + } + + @Override + public Set deserializeErrors(ModuleInfo module) { + ImmutableSet paths = + module.getModuleConfiguration().stream() + .map(moduleInfo -> moduleInfo.dir.resolve("errors.json")) + .collect(ImmutableSet.toImmutableSet()); + Set errors = new HashSet<>(); + paths.forEach( + path -> { + try { + String content = Files.readString(path, Charset.defaultCharset()); + content = "{ \"errors\": [" + content.substring(0, content.length() - 1) + "]}"; + JSONObject jsonObject = (JSONObject) new JSONParser().parse(content); + JSONArray errorsJson = (JSONArray) jsonObject.get("errors"); + errorsJson.forEach(o -> errors.add(deserializeErrorFromJSON((JSONObject) o, context))); + } catch (IOException | ParseException e) { + throw new RuntimeException(e); + } + }); + return errors; + } + + private UCRTaintError deserializeErrorFromJSON(JSONObject errorsJson, Context context) { + String errorType = (String) errorsJson.get("messageKey"); + int offset = ((Long) errorsJson.get("offset")).intValue(); + Region region = + new Region( + (String) ((JSONObject) errorsJson.get("region")).get("member"), + (String) ((JSONObject) errorsJson.get("region")).get("class")); + ImmutableSet.Builder builder = ImmutableSet.builder(); + ((JSONArray) errorsJson.get("fixes")) + .forEach( + o -> { + JSONObject fixJson = (JSONObject) o; + Location location = Location.fromJSON((JSONObject) fixJson.get("location")); + builder.add( + new Fix( + new AddTypeUseMarkerAnnotation( + location, "edu.ucr.cs.riple.taint.ucrtainting.qual.RUntainted"), + errorType, + true)); + }); + return new UCRTaintError(errorType, "", region, offset, builder.build()); + } + + @Override + public void suppressRemainingAnnotations(Config config, AnnotationInjector injector) { + throw new RuntimeException( + "Suppression for remaining errors is not supported for " + NAME + "yet!"); + } + + @Override + public void verifyCheckerCompatibility() { + + } + + @Override + public void prepareConfigFilesForBuild(ImmutableSet configurations) { + // TODO: implement this once configuration on UCRTaint is finalized. + } + + @Override + public UCRTaintError createError( + String errorType, + String errorMessage, + Region region, + int offset, + ImmutableSet resolvingFixes, ModuleInfo moduleInfo) { + return new UCRTaintError(errorType, errorMessage, region, offset, resolvingFixes); + } +} diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaintError.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaintError.java new file mode 100644 index 000000000..6a741337b --- /dev/null +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaintError.java @@ -0,0 +1,43 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nima Karimipour + * + * 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.core.checkers.ucrtaint; + +import com.google.common.collect.ImmutableSet; +import edu.ucr.cs.riple.core.metadata.index.Error; +import edu.ucr.cs.riple.core.metadata.index.Fix; +import edu.ucr.cs.riple.core.metadata.trackers.Region; + +/** Represents an error reported by {@link UCRTaint}. */ +public class UCRTaintError extends Error { + + public UCRTaintError( + String messageType, + String message, + Region region, + int offset, + ImmutableSet resolvingFixes) { + super(messageType, message, region, offset, resolvingFixes); + } +} From da014751b7d5dbc4c27e24ee93e2b24f01c8bce5 Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Thu, 18 May 2023 14:14:46 -0700 Subject: [PATCH 30/35] rename and refactor --- .../java/edu/ucr/cs/riple/core/Annotator.java | 2 +- .../ucr/cs/riple/core/checkers/Checker.java | 2 +- .../core/checkers/nullaway/NullAway.java | 2 +- .../core/checkers/ucrtaint/UCRTaint.java | 188 +++++++++--------- 4 files changed, 96 insertions(+), 98 deletions(-) diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Annotator.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Annotator.java index 41dbd8541..d811be205 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Annotator.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Annotator.java @@ -120,7 +120,7 @@ private void annotate() { } } if (config.forceResolveActivated) { - context.checker.suppressRemainingAnnotations(injector); + context.checker.suppressRemainingErrors(injector); } System.out.println("\nFinished annotating."); Utility.writeReports(context, cache.reports().stream().collect(ImmutableSet.toImmutableSet())); diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java index 630b9f580..0f1a0e41c 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java @@ -53,7 +53,7 @@ public interface Checker { * * @param injector Annotation injector to inject selected annotations. */ - void suppressRemainingAnnotations(AnnotationInjector injector); + void suppressRemainingErrors(AnnotationInjector injector); /** * Used to do any pre-processing steps before running the inference. diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java index 2246c5330..f16580c68 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java @@ -223,7 +223,7 @@ protected ImmutableSet generateFixesForUninitializedFields( * @param injector Annotation injector to inject selected annotations. */ @Override - public void suppressRemainingAnnotations(AnnotationInjector injector) { + public void suppressRemainingErrors(AnnotationInjector injector) { // Collect regions with remaining errors. Utility.buildTarget(context); Set remainingErrors = deserializeErrors(context.targetModuleInfo); diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaint.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaint.java index 186d1f03b..40c8effec 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaint.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaint.java @@ -1,34 +1,33 @@ -* - * MIT License - * - * Copyright (c) 2023 Nima Karimipour - * - * 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. - */ +/* + * MIT License + * + * Copyright (c) 2023 Nima Karimipour + * + * 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.core.checkers.ucrtaint; +package edu.ucr.cs.riple.core.checkers.ucrtaint; import com.google.common.collect.ImmutableSet; import edu.ucr.cs.riple.core.Config; import edu.ucr.cs.riple.core.Context; import edu.ucr.cs.riple.core.checkers.CheckerBaseClass; -import edu.ucr.cs.riple.core.checkers.ucrtaint.UCRTaintError; import edu.ucr.cs.riple.core.injectors.AnnotationInjector; import edu.ucr.cs.riple.core.metadata.index.Fix; import edu.ucr.cs.riple.core.metadata.trackers.Region; @@ -53,81 +52,80 @@ */ public class UCRTaint extends CheckerBaseClass { - /** The name of the checker. This is used to identify the checker in the configuration file. */ - public static final String NAME = "UCRTaint"; - - public UCRTaint(Context context) { - super(context); - } + /** The name of the checker. This is used to identify the checker in the configuration file. */ + public static final String NAME = "UCRTaint"; - @Override - public Set deserializeErrors(ModuleInfo module) { - ImmutableSet paths = - module.getModuleConfiguration().stream() - .map(moduleInfo -> moduleInfo.dir.resolve("errors.json")) - .collect(ImmutableSet.toImmutableSet()); - Set errors = new HashSet<>(); - paths.forEach( - path -> { - try { - String content = Files.readString(path, Charset.defaultCharset()); - content = "{ \"errors\": [" + content.substring(0, content.length() - 1) + "]}"; - JSONObject jsonObject = (JSONObject) new JSONParser().parse(content); - JSONArray errorsJson = (JSONArray) jsonObject.get("errors"); - errorsJson.forEach(o -> errors.add(deserializeErrorFromJSON((JSONObject) o, context))); - } catch (IOException | ParseException e) { - throw new RuntimeException(e); - } - }); - return errors; - } + public UCRTaint(Context context) { + super(context); + } - private UCRTaintError deserializeErrorFromJSON(JSONObject errorsJson, Context context) { - String errorType = (String) errorsJson.get("messageKey"); - int offset = ((Long) errorsJson.get("offset")).intValue(); - Region region = - new Region( - (String) ((JSONObject) errorsJson.get("region")).get("member"), - (String) ((JSONObject) errorsJson.get("region")).get("class")); - ImmutableSet.Builder builder = ImmutableSet.builder(); - ((JSONArray) errorsJson.get("fixes")) - .forEach( - o -> { - JSONObject fixJson = (JSONObject) o; - Location location = Location.fromJSON((JSONObject) fixJson.get("location")); - builder.add( - new Fix( - new AddTypeUseMarkerAnnotation( - location, "edu.ucr.cs.riple.taint.ucrtainting.qual.RUntainted"), - errorType, - true)); - }); - return new UCRTaintError(errorType, "", region, offset, builder.build()); - } + @Override + public Set deserializeErrors(ModuleInfo module) { + ImmutableSet paths = + module.getModuleConfiguration().stream() + .map(moduleInfo -> moduleInfo.dir.resolve("errors.json")) + .collect(ImmutableSet.toImmutableSet()); + Set errors = new HashSet<>(); + paths.forEach( + path -> { + try { + String content = Files.readString(path, Charset.defaultCharset()); + content = "{ \"errors\": [" + content.substring(0, content.length() - 1) + "]}"; + JSONObject jsonObject = (JSONObject) new JSONParser().parse(content); + JSONArray errorsJson = (JSONArray) jsonObject.get("errors"); + errorsJson.forEach(o -> errors.add(deserializeErrorFromJSON((JSONObject) o, context))); + } catch (IOException | ParseException e) { + throw new RuntimeException(e); + } + }); + return errors; + } - @Override - public void suppressRemainingAnnotations(Config config, AnnotationInjector injector) { - throw new RuntimeException( - "Suppression for remaining errors is not supported for " + NAME + "yet!"); - } + private UCRTaintError deserializeErrorFromJSON(JSONObject errorsJson, Context context) { + String errorType = (String) errorsJson.get("messageKey"); + int offset = ((Long) errorsJson.get("offset")).intValue(); + Region region = + new Region( + (String) ((JSONObject) errorsJson.get("region")).get("member"), + (String) ((JSONObject) errorsJson.get("region")).get("class")); + ImmutableSet.Builder builder = ImmutableSet.builder(); + ((JSONArray) errorsJson.get("fixes")) + .forEach( + o -> { + JSONObject fixJson = (JSONObject) o; + Location location = Location.fromJSON((JSONObject) fixJson.get("location")); + builder.add( + new Fix( + new AddTypeUseMarkerAnnotation( + location, "edu.ucr.cs.riple.taint.ucrtainting.qual.RUntainted"), + errorType, + true)); + }); + return new UCRTaintError(errorType, "", region, offset, builder.build()); + } - @Override - public void verifyCheckerCompatibility() { + @Override + public void suppressRemainingAnnotations(Config config, AnnotationInjector injector) { + throw new RuntimeException( + "Suppression for remaining errors is not supported for " + NAME + "yet!"); + } - } + @Override + public void verifyCheckerCompatibility() {} - @Override - public void prepareConfigFilesForBuild(ImmutableSet configurations) { - // TODO: implement this once configuration on UCRTaint is finalized. - } + @Override + public void prepareConfigFilesForBuild(ImmutableSet configurations) { + // TODO: implement this once configuration on UCRTaint is finalized. + } - @Override - public UCRTaintError createError( - String errorType, - String errorMessage, - Region region, - int offset, - ImmutableSet resolvingFixes, ModuleInfo moduleInfo) { - return new UCRTaintError(errorType, errorMessage, region, offset, resolvingFixes); - } + @Override + public UCRTaintError createError( + String errorType, + String errorMessage, + Region region, + int offset, + ImmutableSet resolvingFixes, + ModuleInfo moduleInfo) { + return new UCRTaintError(errorType, errorMessage, region, offset, resolvingFixes); + } } From 9dd87ad65838e32954903b78e2e5010a3668e3da Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Thu, 18 May 2023 14:15:17 -0700 Subject: [PATCH 31/35] rename --- .../src/main/java/edu/ucr/cs/riple/core/Annotator.java | 2 +- .../src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java | 2 +- .../java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Annotator.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Annotator.java index 41dbd8541..d811be205 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/Annotator.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/Annotator.java @@ -120,7 +120,7 @@ private void annotate() { } } if (config.forceResolveActivated) { - context.checker.suppressRemainingAnnotations(injector); + context.checker.suppressRemainingErrors(injector); } System.out.println("\nFinished annotating."); Utility.writeReports(context, cache.reports().stream().collect(ImmutableSet.toImmutableSet())); diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java index 630b9f580..0f1a0e41c 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/Checker.java @@ -53,7 +53,7 @@ public interface Checker { * * @param injector Annotation injector to inject selected annotations. */ - void suppressRemainingAnnotations(AnnotationInjector injector); + void suppressRemainingErrors(AnnotationInjector injector); /** * Used to do any pre-processing steps before running the inference. diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java index 2246c5330..f16580c68 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/nullaway/NullAway.java @@ -223,7 +223,7 @@ protected ImmutableSet generateFixesForUninitializedFields( * @param injector Annotation injector to inject selected annotations. */ @Override - public void suppressRemainingAnnotations(AnnotationInjector injector) { + public void suppressRemainingErrors(AnnotationInjector injector) { // Collect regions with remaining errors. Utility.buildTarget(context); Set remainingErrors = deserializeErrors(context.targetModuleInfo); From 10e7098466bd3feaa1261b8b321786d3df41bc72 Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Thu, 18 May 2023 14:30:13 -0700 Subject: [PATCH 32/35] update --- .../core/checkers/ucrtaint/UCRTaint.java | 14 +++++--- .../cs/riple/injector/location/Location.java | 33 +++++++++++++++++++ .../cs/riple/injector/location/OnClass.java | 5 +++ .../cs/riple/injector/location/OnField.java | 7 ++++ .../injector/location/OnLocalVariable.java | 7 ++++ .../cs/riple/injector/location/OnMethod.java | 7 ++++ .../riple/injector/location/OnParameter.java | 7 ++++ 7 files changed, 75 insertions(+), 5 deletions(-) diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaint.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaint.java index 40c8effec..9a892673b 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaint.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaint.java @@ -25,7 +25,6 @@ package edu.ucr.cs.riple.core.checkers.ucrtaint; import com.google.common.collect.ImmutableSet; -import edu.ucr.cs.riple.core.Config; import edu.ucr.cs.riple.core.Context; import edu.ucr.cs.riple.core.checkers.CheckerBaseClass; import edu.ucr.cs.riple.core.injectors.AnnotationInjector; @@ -59,6 +58,9 @@ public UCRTaint(Context context) { super(context); } + @Override + public void preprocess(AnnotationInjector injector) {} + @Override public Set deserializeErrors(ModuleInfo module) { ImmutableSet paths = @@ -73,7 +75,7 @@ public Set deserializeErrors(ModuleInfo module) { content = "{ \"errors\": [" + content.substring(0, content.length() - 1) + "]}"; JSONObject jsonObject = (JSONObject) new JSONParser().parse(content); JSONArray errorsJson = (JSONArray) jsonObject.get("errors"); - errorsJson.forEach(o -> errors.add(deserializeErrorFromJSON((JSONObject) o, context))); + errorsJson.forEach(o -> errors.add(deserializeErrorFromJSON((JSONObject) o, module))); } catch (IOException | ParseException e) { throw new RuntimeException(e); } @@ -81,7 +83,7 @@ public Set deserializeErrors(ModuleInfo module) { return errors; } - private UCRTaintError deserializeErrorFromJSON(JSONObject errorsJson, Context context) { + private UCRTaintError deserializeErrorFromJSON(JSONObject errorsJson, ModuleInfo moduleInfo) { String errorType = (String) errorsJson.get("messageKey"); int offset = ((Long) errorsJson.get("offset")).intValue(); Region region = @@ -93,7 +95,9 @@ private UCRTaintError deserializeErrorFromJSON(JSONObject errorsJson, Context co .forEach( o -> { JSONObject fixJson = (JSONObject) o; - Location location = Location.fromJSON((JSONObject) fixJson.get("location")); + Location location = + Location.createLocationFromJSON((JSONObject) fixJson.get("location")); + location.ifField(onField -> extendVariableList(onField, moduleInfo)); builder.add( new Fix( new AddTypeUseMarkerAnnotation( @@ -105,7 +109,7 @@ private UCRTaintError deserializeErrorFromJSON(JSONObject errorsJson, Context co } @Override - public void suppressRemainingAnnotations(Config config, AnnotationInjector injector) { + public void suppressRemainingErrors(AnnotationInjector injector) { throw new RuntimeException( "Suppression for remaining errors is not supported for " + NAME + "yet!"); } 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 a32c7ef81..add5b665b 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 @@ -28,10 +28,12 @@ import com.google.common.collect.Sets; import edu.ucr.cs.riple.injector.Helper; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.Objects; import java.util.function.Consumer; import javax.annotation.Nullable; +import org.json.simple.JSONObject; /** Represents a location of an element in the source code. */ public abstract class Location { @@ -57,6 +59,19 @@ public Location(LocationKind type, Path path, String clazz) { this.path = path; } + /** + * Creates an instance of {@link Location} for a given type, and retrieves path and class values + * from the given JSON object. + * + * @param type The type of the location. + * @param json The JSON object containing the path and class values. + */ + public Location(LocationKind type, JSONObject json) { + this.type = type; + this.clazz = (String) json.get("class"); + this.path = Paths.get((String) json.get("path")); + } + /** * Creates an instance of {@link Location} based on values written in a row of a TSV file. These * values should be in order of: @@ -98,6 +113,24 @@ public static Location createLocationFromArrayInfo(String[] values) { throw new RuntimeException("Cannot reach this statement, values: " + Arrays.toString(values)); } + public static Location createLocationFromJSON(JSONObject json) { + String kind = (String) json.get("kind"); + switch (kind) { + case "METHOD": + return new OnMethod(json); + case "FIELD": + return new OnField(json); + case "PARAMETER": + return new OnParameter(json); + case "LOCAL_VARIABLE": + return new OnLocalVariable(json); + case "CLASS": + return new OnClass(json); + default: + throw new RuntimeException("Cannot reach this statement, kind: " + kind); + } + } + /** * 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 e5cffb497..0ed276afa 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 @@ -27,6 +27,7 @@ import edu.ucr.cs.riple.injector.Helper; import java.nio.file.Path; import java.util.regex.Pattern; +import org.json.simple.JSONObject; /** Represents a location for class element. This location is used to apply changes to a class. */ public class OnClass extends Location { @@ -45,6 +46,10 @@ public OnClass(String path, String clazz) { this(Helper.deserializePath(path), clazz); } + public OnClass(JSONObject json) { + super(LocationKind.CLASS, json); + } + /** * 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 9331cfddc..1a87d3e7f 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 @@ -27,9 +27,11 @@ import edu.ucr.cs.riple.injector.Helper; import java.nio.file.Path; import java.util.Collections; +import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.function.Consumer; +import org.json.simple.JSONObject; /** * Represents a location for field element. This location is used to apply changes to a class field. @@ -59,6 +61,11 @@ public OnField(String path, String clazz, Set variables) { this(Helper.deserializePath(path), clazz, variables); } + public OnField(JSONObject json) { + super(LocationKind.FIELD, json); + this.variables = new HashSet<>(Collections.singletonList((String) json.get("variables"))); + } + @Override public void ifField(Consumer consumer) { consumer.accept(this); diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnLocalVariable.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnLocalVariable.java index 292b6c373..0e313b93f 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnLocalVariable.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnLocalVariable.java @@ -28,6 +28,7 @@ import java.nio.file.Path; import java.util.Objects; import java.util.function.Consumer; +import org.json.simple.JSONObject; /** * Represents a location for local variable element. This location is used to apply changes to a @@ -55,6 +56,12 @@ public OnLocalVariable(String path, String clazz, String encMethod, String varNa this(Helper.deserializePath(path), clazz, encMethod, varName); } + public OnLocalVariable(JSONObject json) { + super(LocationKind.LOCAL_VARIABLE, json); + this.encMethod = new OnMethod(json); + this.varName = (String) json.get("varName"); + } + @Override public void ifLocalVariable(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 f3c2c1cfc..d25ba12e5 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 @@ -30,6 +30,7 @@ import java.nio.file.Path; import java.util.Objects; import java.util.function.Consumer; +import org.json.simple.JSONObject; /** Represents a location for method element. This location is used to apply changes to a method. */ public class OnMethod extends Location { @@ -52,6 +53,12 @@ public OnMethod(String path, String clazz, String method) { this(Helper.deserializePath(path), clazz, method); } + public OnMethod(JSONObject json) { + super(LocationKind.METHOD, json); + this.method = (String) json.get("method"); + this.matcher = new SignatureMatcher(method); + } + /** * Checks if the given method matches the method signature of this location. * 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 4719c12a0..e52c87545 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 @@ -28,6 +28,7 @@ import java.nio.file.Path; import java.util.Objects; import java.util.function.Consumer; +import org.json.simple.JSONObject; /** * Represents a location for parameter element. This location is used to apply changes to a @@ -50,6 +51,12 @@ public OnParameter(String path, String clazz, String method, int index) { this(Helper.deserializePath(path), clazz, method, index); } + public OnParameter(JSONObject json) { + super(LocationKind.PARAMETER, json); + this.enclosingMethod = new OnMethod(json); + this.index = ((Long) json.get("index")).intValue(); + } + @Override public void ifParameter(Consumer consumer) { consumer.accept(this); From bc8b43fa26896c6b43678022b4df890319d61c0a Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Thu, 18 May 2023 14:36:24 -0700 Subject: [PATCH 33/35] update --- .../metadata/trackers/CompoundTracker.java | 1 + .../trackers/LocalVariableTracker.java | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/trackers/LocalVariableTracker.java diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/trackers/CompoundTracker.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/trackers/CompoundTracker.java index cca61f387..4938f7af7 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/trackers/CompoundTracker.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/trackers/CompoundTracker.java @@ -47,6 +47,7 @@ public CompoundTracker(Config config, ModuleInfo moduleInfo) { MethodRegionTracker methodRegionTracker = new MethodRegionTracker(moduleInfo); this.trackers = ImmutableSet.of( + new LocalVariableTracker(), new FieldRegionTracker(moduleInfo), methodRegionTracker, new ParameterRegionTracker(moduleInfo, methodRegionTracker)); diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/trackers/LocalVariableTracker.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/trackers/LocalVariableTracker.java new file mode 100644 index 000000000..a598ef8be --- /dev/null +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/trackers/LocalVariableTracker.java @@ -0,0 +1,43 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nima Karimipour + * + * 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.core.metadata.trackers; + +import edu.ucr.cs.riple.injector.location.Location; +import edu.ucr.cs.riple.injector.location.OnLocalVariable; +import java.util.Optional; +import java.util.Set; + +/** Tracker for Local variables. */ +public class LocalVariableTracker implements RegionTracker { + + @Override + public Optional> getRegions(Location location) { + if (!location.isOnLocalVariable()) { + return Optional.empty(); + } + OnLocalVariable localVariable = location.toLocalVariable(); + return Optional.of(Set.of(new Region(localVariable.clazz, localVariable.encMethod.method))); + } +} From 079f304392fa1f23b8e84c697b20a4e1abbde2f0 Mon Sep 17 00:00:00 2001 From: Nima Karimipour Date: Thu, 18 May 2023 14:41:22 -0700 Subject: [PATCH 34/35] add UCRTaint to get checker by name --- .../java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java index 971d0d44b..94d2c20a1 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java @@ -27,6 +27,7 @@ import edu.ucr.cs.riple.core.Config; import edu.ucr.cs.riple.core.Context; import edu.ucr.cs.riple.core.checkers.nullaway.NullAway; +import edu.ucr.cs.riple.core.checkers.ucrtaint.UCRTaint; import edu.ucr.cs.riple.core.metadata.index.Error; import edu.ucr.cs.riple.core.module.ModuleInfo; import edu.ucr.cs.riple.injector.location.OnField; @@ -75,6 +76,8 @@ public static Checker getCheckerByName(String name, Context context) { switch (name) { case NullAway.NAME: return new NullAway(context); + case UCRTaint.NAME: + return new UCRTaint(context); default: throw new RuntimeException("Unknown checker name: " + name); } From 2c85855dd5efa252e0045cc8cbd7830d8457c1ff Mon Sep 17 00:00:00 2001 From: nimakarimipour Date: Tue, 30 May 2023 19:06:35 -0700 Subject: [PATCH 35/35] remove unwanted changes --- .../java/edu/ucr/cs/riple/injector/TypeUseAnnotationTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/injector/src/test/java/edu/ucr/cs/riple/injector/TypeUseAnnotationTest.java b/injector/src/test/java/edu/ucr/cs/riple/injector/TypeUseAnnotationTest.java index d106fc394..d18c969c2 100644 --- a/injector/src/test/java/edu/ucr/cs/riple/injector/TypeUseAnnotationTest.java +++ b/injector/src/test/java/edu/ucr/cs/riple/injector/TypeUseAnnotationTest.java @@ -196,7 +196,6 @@ public void additionOnArrayTest() { "public class Foo {", " public void foo() {", " java.util.Map f0;", - " T[] f1;", " Map[] f1;", " String[] f2;", " }", @@ -207,7 +206,6 @@ public void additionOnArrayTest() { "public class Foo {", " public void foo() {", " java.util.@UnTainted Map f0;", - " @UnTainted T[] f1;", " @UnTainted Map<@UnTainted T, @UnTainted T>[] f1;", " @UnTainted String[] f2;", " }",