Skip to content

Commit

Permalink
Update region computation. (#91)
Browse files Browse the repository at this point in the history
This PR updates the region computation in `Type-Annotator-Scanner` according to latest changes in NullAway following [PR658](uber/NullAway#658). 

Also in this PR the infrastructure for keeping compatibility with older versions of NullAway (`0.10.4` or less) is implemented.
  • Loading branch information
nimakarimipour authored Nov 23, 2022
1 parent ffbefb6 commit e4b482e
Show file tree
Hide file tree
Showing 36 changed files with 788 additions and 197 deletions.
13 changes: 12 additions & 1 deletion .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,15 @@ jobs:
uses: actions/upload-artifact@v2
with:
name: Test report
path: build/reports/tests/test
path: build/reports/tests/test
build-with-nullaway-serialization-0:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: 11
distribution: 'temurin'
- name: Build with Gradle
run: ./gradlew :core:test --scan -Pnullaway-serialization-format-version=0
18 changes: 17 additions & 1 deletion core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,15 @@ dependencies {
testImplementation deps.build.commonsio
}

// Should be the latest supporting version of NullAway.
def NULLAWAY_TEST = "0.10.5"

tasks.test.dependsOn(':type-annotator-scanner:publishToMavenLocal')
tasks.test.dependsOn(':qual:publishToMavenLocal')

// Set up environment variables for test configuration tu run with the latest development version.
tasks.test.doFirst {
environment "NULLAWAY_TEST_VERSION", "0.10.0"
environment "NULLAWAY_TEST_VERSION", NULLAWAY_TEST
environment "ANNOTATOR_VERSION", project.version
}

Expand Down Expand Up @@ -83,3 +86,16 @@ jar {
shadowJar {
archiveClassifier = null
}

// Configure test environment
def nullawayVersionMap = [0:"0.10.4", 1:"0.10.5"]
tasks.create("configureNullAwayVersion"){
if(!project.hasProperty("nullaway-serialization-format-version")){
return
}
NULLAWAY_TEST = nullawayVersionMap.get((project.getProperty("nullaway-serialization-format-version")) as int)
println "NullAway Test version changed to: " + NULLAWAY_TEST

// exclude unsupported tests below...
}
tasks.test.dependsOn("configureNullAwayVersion")
26 changes: 11 additions & 15 deletions core/src/main/java/edu/ucr/cs/riple/core/Annotator.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
import edu.ucr.cs.riple.injector.changes.AddSingleElementAnnotation;
import edu.ucr.cs.riple.injector.location.OnField;
import edu.ucr.cs.riple.injector.location.OnMethod;
import edu.ucr.cs.riple.scanner.Serializer;
import java.util.List;
import java.util.Objects;
import java.util.Set;
Expand Down Expand Up @@ -100,13 +99,13 @@ private void preprocess() {
Utility.setScannerCheckerActivation(config.target, true);
System.out.println("Making the first build...");
Utility.buildTarget(config, true);
config.initializeAdapter();
Set<OnField> uninitializedFields =
Utility.readFixesFromOutputDirectory(config.target, Fix.factory(config, null)).stream()
.filter(fix -> fix.isOnField() && fix.reasons.contains("FIELD_NO_INIT"))
.map(Fix::toField)
.collect(Collectors.toSet());
FieldInitializationAnalysis analysis =
new FieldInitializationAnalysis(config.target.dir.resolve("field_init.tsv"));
FieldInitializationAnalysis analysis = new FieldInitializationAnalysis(config);
Set<AddAnnotation> initializers =
analysis
.findInitializers(uninitializedFields)
Expand All @@ -120,9 +119,9 @@ private void annotate() {
Utility.setScannerCheckerActivation(config.target, true);
Utility.buildTarget(config);
Utility.setScannerCheckerActivation(config.target, false);
FieldDeclarationAnalysis fieldDeclarationAnalysis = new FieldDeclarationAnalysis(config.target);
MethodDeclarationTree tree =
new MethodDeclarationTree(config.target.dir.resolve(Serializer.METHOD_INFO_FILE_NAME));
FieldDeclarationAnalysis fieldDeclarationAnalysis =
new FieldDeclarationAnalysis(config, config.target);
MethodDeclarationTree tree = new MethodDeclarationTree(config);
// globalAnalyzer analyzes effects of all public APIs on downstream dependencies.
// Through iterations, since the source code for downstream dependencies does not change and the
// computation does not depend on the changes in the target module, it will compute the same
Expand Down Expand Up @@ -220,9 +219,8 @@ private ImmutableSet<Report> processTriggeredFixes(
.collect(ImmutableSet.toImmutableSet());

// Initializing required explorer instances.
MethodDeclarationTree tree =
new MethodDeclarationTree(config.target.dir.resolve(Serializer.METHOD_INFO_FILE_NAME));
RegionTracker tracker = new CompoundTracker(config.target, tree);
MethodDeclarationTree tree = new MethodDeclarationTree(config);
RegionTracker tracker = new CompoundTracker(config, config.target, tree);
TargetModuleSupplier supplier = new TargetModuleSupplier(config, tree);
Explorer explorer =
config.exhaustiveSearch
Expand Down Expand Up @@ -253,7 +251,7 @@ private void forceResolveRemainingErrors(
FieldDeclarationAnalysis fieldDeclarationAnalysis, MethodDeclarationTree tree) {
// Collect regions with remaining errors.
Utility.buildTarget(config);
List<Error> remainingErrors = Utility.readErrorsFromOutputDirectory(config.target);
List<Error> remainingErrors = Utility.readErrorsFromOutputDirectory(config, config.target);
Set<Fix> remainingFixes =
Utility.readFixesFromOutputDirectory(
config.target, Fix.factory(config, fieldDeclarationAnalysis));
Expand All @@ -266,8 +264,8 @@ private void forceResolveRemainingErrors(
// find the corresponding method nodes.
.map(
error -> {
if (!error.encMethod().equals("null")) {
return tree.findNode(error.encMethod(), error.encClass());
if (error.getRegion().isOnMethod()) {
return tree.findNode(error.encMember(), error.encClass());
}
if (error.nonnullTarget == null) {
// Just a sanity check.
Expand Down Expand Up @@ -299,7 +297,7 @@ private void forceResolveRemainingErrors(
return false;
}
OnField onField = fix.toField();
return onField.clazz.equals(fix.encClass()) && fix.encMethod().equals("");
return onField.clazz.equals(fix.encClass()) && !fix.getRegion().isOnMethod();
})
.map(
fix ->
Expand All @@ -322,8 +320,6 @@ private void forceResolveRemainingErrors(
fix ->
new AddSingleElementAnnotation(
fix.toField(), "SuppressWarnings", "NullAway.Init", false))
// Exclude already annotated fields with a general NullAway suppress warning.
.filter(f -> !suppressWarningsAnnotations.contains(f))
.collect(Collectors.toSet());
injector.injectAnnotations(initializationSuppressWarningsAnnotations);
// Update log.
Expand Down
52 changes: 52 additions & 0 deletions core/src/main/java/edu/ucr/cs/riple/core/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import edu.ucr.cs.riple.core.adapters.NullAwayV0Adapter;
import edu.ucr.cs.riple.core.adapters.NullAwayV1Adapter;
import edu.ucr.cs.riple.core.adapters.NullAwayVersionAdapter;
import edu.ucr.cs.riple.core.log.Log;
import edu.ucr.cs.riple.core.util.Utility;
import java.io.BufferedWriter;
Expand Down Expand Up @@ -138,6 +141,12 @@ public class Config {
*/
public final boolean inferenceActivated;

/**
* This adapter is initialized lazily as it requires build of target to serialize its using
* NullAway serialization version.
*/
private NullAwayVersionAdapter adapter;

/**
* Builds config from command line arguments.
*
Expand Down Expand Up @@ -483,6 +492,49 @@ public Config(Path configPath) {
log.reset();
}

/** Initializes NullAway serialization adapter according to the serialized version. */
public void initializeAdapter() {
if (adapter != null) {
// adapter is already initialized.
return;
}
Path serializationVersionPath = target.dir.resolve("serialization_version.txt");
if (!serializationVersionPath.toFile().exists()) {
// Older versions of NullAway.
this.adapter = new NullAwayV0Adapter(this);
return;
}
try {
List<String> lines = Files.readAllLines(serializationVersionPath);
int version = Integer.parseInt(lines.get(0));
switch (version) {
case 0:
this.adapter = new NullAwayV0Adapter(this);
break;
case 1:
this.adapter = new NullAwayV1Adapter(this);
break;
default:
throw new RuntimeException("Unrecognized NullAway serialization version: " + version);
}
} catch (IOException e) {
throw new RuntimeException(
"Could not read serialization version at path: " + serializationVersionPath, e);
}
}

/**
* Getter for adapter.
*
* @return adapter.
*/
public NullAwayVersionAdapter getAdapter() {
if (adapter == null) {
throw new IllegalStateException("Adapter is not initialized.");
}
return adapter;
}

/**
* Returns the latest id associated to a module, used to create unique ids for each module and
* increments it.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* 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.adapters;

import com.google.common.base.Preconditions;
import edu.ucr.cs.riple.core.Config;
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.metadata.trackers.TrackerNode;
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.util.Arrays;
import java.util.Collections;
import java.util.Set;

/**
* Adapter working with versions below:
*
* <ul>
* <li>NullAway: Serialization version 0
* <li>Type Annotator Scanner: 1.3.3 or below
* </ul>
*/
public class NullAwayV0Adapter implements NullAwayVersionAdapter {

/** Annotator config. */
private final Config config;

public NullAwayV0Adapter(Config config) {
this.config = config;
}

@Override
public Fix deserializeFix(Location location, String[] values) {
Preconditions.checkArgument(
values.length == 10,
"Expected 10 values to create Fix instance in NullAway serialization version 0 but found: "
+ values.length);
Preconditions.checkArgument(
values[7].equals("nullable"), "unsupported annotation: " + values[7]);
String encMember = !Region.getType(values[9]).equals(Region.Type.METHOD) ? "null" : values[9];
return new Fix(
new AddMarkerAnnotation(location, config.nullableAnnot),
values[6],
new Region(values[8], encMember),
true);
}

@Override
public Error deserializeError(String[] values) {
Preconditions.checkArgument(
values.length == 10,
"Expected 10 values to create Error instance in NullAway serialization version 0 but found: "
+ values.length);
String encMember = !Region.getType(values[3]).equals(Region.Type.METHOD) ? "null" : values[3];
return new Error(
values[0],
values[1],
new Region(values[2], encMember),
Location.createLocationFromArrayInfo(Arrays.copyOfRange(values, 4, 10)));
}

@Override
public TrackerNode deserializeTrackerNode(String[] values) {
Preconditions.checkArgument(
values.length == 4,
"Expected 4 values to create TrackerNode instance in NullAway serialization version 0 but found: "
+ values.length);
String encMember = !Region.getType(values[1]).equals(Region.Type.METHOD) ? "null" : values[1];
return new TrackerNode(values[0], encMember, values[2], values[3]);
}

@Override
public Set<Region> getFieldRegionScope(OnField onField) {
return Collections.singleton(new Region(onField.clazz, "null"));
}
}
Loading

0 comments on commit e4b482e

Please sign in to comment.