Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 96 additions & 15 deletions java/daikon/AnnotateNullable.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package daikon;

import daikon.PptTopLevel.PptType;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -46,6 +46,12 @@ private AnnotateNullable() {
/** Map from a class name to the list of static functions for that class. */
static Map<String, List<PptTopLevel>> class_map = new LinkedHashMap<>();

/**
* Map from the name of a combined exit ppt that the .inv file lacks, to the numbered exit ppts of
* that method. An older .inv file may lack a combined exit ppt.
*/
static Map<PptName, List<PptTopLevel>> numbered_exits = new LinkedHashMap<>();

// The package for the previous class. Used to reduce duplication in
// output file.
static @Interned String last_package = "";
Expand Down Expand Up @@ -93,6 +99,15 @@ public static void main(String[] args) throws IOException {
System.out.println();
}

for (PptTopLevel ppt : ppts.pptIterable()) {
if (ppt.is_subexit()) {
PptName exit_name = ppt.ppt_name.makeExit();
if (ppts.get(exit_name) == null) {
numbered_exits.computeIfAbsent(exit_name, k -> new ArrayList<>()).add(ppt);
}
}
}

// Find all exit ppts that do not have a parent and determine what
// class they are associated with. These are static methods for classes
// without any static variables (no class ppt is created if there are no
Expand All @@ -112,7 +127,7 @@ public static void main(String[] args) throws IOException {
// static method can be identified because it will not have the OBJECT
// point as a parent.
for (PptTopLevel ppt : ppts.pptIterable()) {
if (!ppt.is_combined_exit() || !is_static_method(ppt)) {
if (!is_method_exit(ppt) || !is_static_method(ppt)) {
continue;
}

Expand Down Expand Up @@ -149,8 +164,8 @@ public static void main(String[] args) throws IOException {
for (int i = 0; i < ppt.children.size(); i++) {
PptRelation child_rel = ppt.children.get(i);
PptTopLevel child = child_rel.child;
// Skip enter ppts, all of the info is at the exit.
if ((child.type == PptType.ENTER) || (child.type == PptType.OBJECT)) {
// Skip enter ppts and others; all the info is at the exit ppt that represents the method.
if (!is_method_exit(child)) {
continue;
}
child_cnt++;
Expand Down Expand Up @@ -236,8 +251,8 @@ public static void process_class(PptTopLevel object_ppt) {
if (class_ppt != null) {
for (PptRelation child_rel : class_ppt.children) {
PptTopLevel child = child_rel.child;
// Skip enter ppts, all of the info is at the exit.
if ((child.type == PptType.ENTER) || (child.type == PptType.OBJECT)) {
// Skip enter ppts and others; all the info is at the exit ppt that represents the method.
if (!is_method_exit(child)) {
continue;
}
// debug.log("processing static method %s, type %s", child, child.type);
Expand All @@ -257,8 +272,8 @@ public static void process_class(PptTopLevel object_ppt) {
// Process member (non-static) methods
for (PptRelation child_rel : object_ppt.children) {
PptTopLevel child = child_rel.child;
// Skip enter ppts, all of the info is at the exit.
if (child.type == PptType.ENTER) {
// Skip enter ppts and others; all the info is at the exit ppt that represents the method.
if (!is_method_exit(child)) {
continue;
}
// debug.log("processing method %s, type %s", child, child.type);
Expand All @@ -278,14 +293,34 @@ public static void process_class(PptTopLevel object_ppt) {
* string if no annotation is applicable. Otherwise, the return value contains a trailing space.
*/
public static String get_annotation(PptTopLevel ppt, VarInfo vi) {
return get_annotation(Collections.singletonList(ppt), vi);
}

/**
* Returns the annotation for the specified variable, considering all the given exit ppts of a
* method. Returns @Nullable if, at any of the ppts, samples were found for the variable and at
* least one sample contained a null value. Otherwise behaves like {@link
* #get_annotation(PptTopLevel, VarInfo)}.
*
* @param exits the exit ppts of a method, which all declare a variable named like {@code vi}
* @param vi a variable of the first of the exit ppts
* @return the annotation for the variable
*/
public static String get_annotation(List<PptTopLevel> exits, VarInfo vi) {

if (vi.type.isPrimitive()) {
return "";
}

String annotation = (nonnull_annotations ? "NonNull" : "");
if ((ppt.num_samples(vi) > 0) && !ppt.is_nonzero(vi)) {
annotation = "Nullable";
for (PptTopLevel ppt : exits) {
VarInfo ppt_vi = ppt.find_var_by_name(vi.name());
if (ppt_vi == null) {
continue;
}
if ((ppt.num_samples(ppt_vi) > 0) && !ppt.is_nonzero(ppt_vi)) {
annotation = "Nullable";
}
}
if (annotation != "") { // interned
// if (! stub_format) {
Expand All @@ -299,7 +334,12 @@ public static String get_annotation(PptTopLevel ppt, VarInfo vi) {
/** Print out the annotations for the specified method. */
public static void process_method(PptTopLevel ppt) {

assert ppt.type == PptType.EXIT : ppt;
assert is_method_exit(ppt) : ppt;
List<PptTopLevel> exits = method_exits(ppt);
int num_samples = 0;
for (PptTopLevel exit : exits) {
num_samples += exit.num_samples();
}

// Get all of the parameters to the method and the return value
List<VarInfo> params = new ArrayList<>();
Expand All @@ -317,7 +357,7 @@ public static void process_method(PptTopLevel ppt) {
}

// The formatted annotation for the return value with a leading space, or empty string
String return_annotation = (retvar == null ? "" : " " + get_annotation(ppt, retvar));
String return_annotation = (retvar == null ? "" : " " + get_annotation(exits, retvar));

// Look up the annotation for each parameter.
List<String> names = new ArrayList<>();
Expand All @@ -326,7 +366,7 @@ public static void process_method(PptTopLevel ppt) {
String annotation = "";
names.add(param.name());
if (param.file_rep_type.isHashcode()) {
annotation = get_annotation(ppt, param);
annotation = get_annotation(exits, param);
}
annos.add(annotation);
}
Expand All @@ -339,9 +379,9 @@ public static void process_method(PptTopLevel ppt) {
sj.add(String.format("%s %s %s", annos.get(i), "type-goes-here", names.get(i)));
}
System.out.printf("%s", sj);
System.out.printf("; // %d samples%n", ppt.num_samples());
System.out.printf("; // %d samples%n", num_samples);
} else {
System.out.printf(" method %s : // %d samples%n", jvm_signature(ppt), ppt.num_samples());
System.out.printf(" method %s : // %d samples%n", jvm_signature(ppt), num_samples);
System.out.printf(" return:%s%n", return_annotation);
for (int i = 0; i < params.size(); i++) {
// Print the annotation for this parameter
Expand Down Expand Up @@ -439,6 +479,47 @@ public static String field_name(VarInfo vi) {
}
}

/**
* Returns true if the ppt is the exit ppt that represents its method. That is usually the
* combined exit ppt. However, an older .inv file may lack a combined exit ppt, in which case the
* numbered exit ppts are direct children of the OBJECT or CLASS ppt, and the first of them
* represents the method.
*
* @param ppt a program point
* @return true if the ppt is the exit ppt that represents its method
* @see #method_exits
*/
@Pure
public static boolean is_method_exit(PptTopLevel ppt) {
if (ppt.is_combined_exit()) {
return true;
}
if (!ppt.is_subexit()) {
return false;
}
List<PptTopLevel> exits = numbered_exits.get(ppt.ppt_name.makeExit());
return exits != null && exits.get(0) == ppt;
}

/**
* Returns the exit ppts that together summarize all exits from the method that the given ppt
* represents: the combined exit ppt, or all the numbered exit ppts if there is no combined exit
* ppt.
*
* @param ppt the exit ppt that represents its method
* @return the exit ppts that together summarize all exits from the method
* @see #is_method_exit
*/
private static List<PptTopLevel> method_exits(PptTopLevel ppt) {
assert is_method_exit(ppt) : ppt;
if (ppt.is_combined_exit()) {
return Collections.singletonList(ppt);
}
@SuppressWarnings("nullness:assignment") // is_method_exit(ppt) guarantees the map has the key
@NonNull List<PptTopLevel> exits = numbered_exits.get(ppt.ppt_name.makeExit());
return exits;
}

/**
* Returns true if the method of the specified ppt is static or not. The ppt must be an exit ppt.
* Exit ppts that do not have an object as a parent are inferred to be static. This does not work
Expand Down
4 changes: 4 additions & 0 deletions java/daikon/Daikon.java
Original file line number Diff line number Diff line change
Expand Up @@ -1876,6 +1876,10 @@ public static void create_combined_exits(PptMap ppts) {

PptTopLevel exitnn_ppt = ppt;
PptName exit_name = ppt.ppt_name.makeExit();
// Don't replace a combined exit point that the declarations already contain.
if (ppts.get(exit_name) != null) {
continue;
}
PptTopLevel exit_ppt = exit_ppts.get(exit_name);

if (debugInit.isLoggable(FINE)) {
Expand Down
1 change: 1 addition & 0 deletions java/daikon/PptConditional.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public PptConditional(PptTopLevel parent, Splitter splitter, boolean splitter_in
super(ctor_name_helper(parent, splitter, splitter_inverse), ctor_vis_helper(parent));
// assert splitter.instantiated() == false;
this.parent = parent;
this.type = parent.type;
@SuppressWarnings({"nullness"}) // won't be used until it's fully initialized
@Initialized PptConditional thisNonRaw = this;
this.splitter = splitter.instantiateSplitter(thisNonRaw);
Expand Down
18 changes: 18 additions & 0 deletions java/daikon/PptName.java
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,23 @@ public boolean isNumberedExitPoint() {
return (point != null) && (isExitPoint() && !isCombinedExitPoint());
}

/**
* Returns true iff this name is that of a numbered exit point: it ends with ":::EXIT" followed by
* one or more digits, such as foo:::EXIT22. By contrast, {@link #isNumberedExitPoint} is also
* true for names such as foo:::EXIT_CONDITION.
*
* @return true iff this name is that of a numbered exit point
*/
@EnsuresNonNullIf(result = true, expression = "point")
@Pure
public boolean isExitWithLineNumber() {
if (!isExitPoint()) {
return false;
}
String line = exitLine();
return !line.isEmpty() && point.length() == FileIO.exit_suffix.length() + line.length();
}

/**
* Returns true iff this name refers to a procedure exit point.
*
Expand All @@ -378,6 +395,7 @@ public boolean isEnterPoint() {
* empty string
* @see #getPointSubscript()
*/
@SideEffectFree
public String exitLine() {
if (!isExitPoint()) {
return "";
Expand Down
82 changes: 54 additions & 28 deletions java/daikon/PptTopLevel.java
Original file line number Diff line number Diff line change
Expand Up @@ -423,14 +423,60 @@ public PptTopLevel(
}
this.ppt_name = new PptName(name);
this.flags = flags;
this.type = type;
this.type = normalize_type(ppt_name, type);
this.parent_relations = parents;
init_vars();
}

/** Restore/Create interns when reading serialized object. */
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
type = normalize_type(ppt_name, type);
}

/**
* Returns the type that a program point with the given name and declared type should have. A
* program point that has no declared type (as in a version 1 decls file), or that is declared
* with type {@link PptType#EXIT} or with the default type {@link PptType#POINT}, gets its type
* from its name:
*
* <ul>
* <li>foo:::ENTER is an {@link PptType#ENTER},
* <li>a combined exit point such as foo:::EXIT is an {@link PptType#EXIT},
* <li>a numbered exit point such as foo:::EXIT22 is a {@link PptType#SUBEXIT},
* <li>Foo:::OBJECT is an {@link PptType#OBJECT},
* <li>Foo:::CLASS is a {@link PptType#CLASS}, and
* <li>any other program point is a generic {@link PptType#POINT}.
* </ul>
*
* (Some front ends declare a numbered exit with type {@link PptType#EXIT}, and a version 2 decls
* file may omit the type.) As a result, the predicates is_subexit, is_combined_exit, etc. agree
* with one another and with the program point's name.
*
* @param ppt_name the name of the program point
* @param type the declared type of the program point, or null if none was declared
* @return the type that the program point should have
*/
private static PptType normalize_type(PptName ppt_name, @Nullable PptType type) {
if (type != null && type != PptType.EXIT && type != PptType.POINT) {
return type;
}
if (ppt_name.isEnterPoint()) {
return PptType.ENTER;
}
if (ppt_name.isCombinedExitPoint()) {
return PptType.EXIT;
}
if (ppt_name.isExitWithLineNumber()) {
return PptType.SUBEXIT;
}
if (ppt_name.isObjectInstanceSynthetic()) {
return PptType.OBJECT;
}
if (ppt_name.isClassStaticSynthetic()) {
return PptType.CLASS;
}
return PptType.POINT;
}

// Used by DaikonSimple, InvMap, and tests. Violates invariants.
Expand All @@ -440,6 +486,7 @@ public PptTopLevel(String name, VarInfo[] var_infos) {
super(var_infos);
this.name = name;
ppt_name = new PptName(name);
type = normalize_type(ppt_name, null);
init_vars();
}

Expand Down Expand Up @@ -4646,11 +4693,7 @@ public void incSampleNumber() {
/** Is this is an exit ppt (combined or specific)? */
@Pure
public boolean is_exit() {
if (type != null) {
return (type == PptType.EXIT) || (type == PptType.SUBEXIT);
} else {
return ppt_name.isExitPoint();
}
return (type == PptType.EXIT) || (type == PptType.SUBEXIT);
}

/**
Expand All @@ -4660,48 +4703,31 @@ public boolean is_exit() {
*/
@Pure
public boolean is_enter() {
if (type != null) {
return (type == PptType.ENTER);
} else {
return ppt_name.isEnterPoint();
}
return type == PptType.ENTER;
}

/** Is this a combined exit point? */
@Pure
public boolean is_combined_exit() {
if (type != null) {
return (type == PptType.EXIT);
} else {
return ppt_name.isCombinedExitPoint();
}
return type == PptType.EXIT;
}

/** Is this a numbered (specific) exit point? */
@Pure
public boolean is_subexit() {
if (type != null) {
return (type == PptType.SUBEXIT);
} else {
return ppt_name.isExitPoint() && !ppt_name.isCombinedExitPoint();
}
return type == PptType.SUBEXIT;
}

/** Is this a ppt that represents an object? */
@Pure
public boolean is_object() {
if (type != null) {
return (type == PptType.OBJECT);
} else {
return ppt_name.isObjectInstanceSynthetic();
}
return type == PptType.OBJECT;
}

/** Is this a ppt that represents a class? */
@EnsuresNonNullIf(result = true, expression = "type")
@Pure
public boolean is_class() {
return (type != null && type == PptType.CLASS);
return type == PptType.CLASS;
}

public String var_names() {
Expand Down
Loading
Loading