Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize field registry population mechanism by parsing each source file only once #230

Merged
merged 7 commits into from
May 15, 2024
Merged
Changes from 6 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
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.BodyDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.utils.Pair;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
Expand Down Expand Up @@ -92,46 +93,63 @@ protected void setup() {

@Override
protected Builder<ClassFieldRecord> getBuilder() {
return values -> {
// Class flat name.
String clazz = values[0];
// Path to class.
Path path = Helper.deserializePath(values[1]);
CompilationUnit tree = Injector.parse(path);
if (tree == null) {
return null;
}
NodeList<BodyDeclaration<?>> members;
try {
members = Helper.getTypeDeclarationMembersByFlatName(tree, clazz);
} catch (TargetClassNotFound notFound) {
System.err.println(notFound.getMessage());
return null;
return new Builder<>() {
Pair<Path, CompilationUnit> lastParsedSourceFile = new Pair<>(null, null);

@Override
public ClassFieldRecord build(String[] values) {
// This method is called with values in format of: [class flat name, path to source file].
// To avoid parsing a source file multiple times, we keep the last parsed
// source file in a reference.
// This optimization is according to the assumption that Scanner
// visits all classes within a single compilation unit tree consecutively.
// Path to class.
Path path = Helper.deserializePath(values[1]);
CompilationUnit tree;
if (lastParsedSourceFile.a != null && lastParsedSourceFile.a.equals(path)) {
// Already visited.
tree = lastParsedSourceFile.b;
} else {
tree = Injector.parse(path);
}
lastParsedSourceFile = new Pair<>(path, tree);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this line be inside the preceding else block?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (tree == null) {
return null;
}
NodeList<BodyDeclaration<?>> members;
// Class flat name.
String clazz = values[0];
try {
members = Helper.getTypeDeclarationMembersByFlatName(tree, clazz);
} catch (TargetClassNotFound notFound) {
System.err.println(notFound.getMessage());
return null;
}
ClassFieldRecord record = new ClassFieldRecord(path, clazz);
members.forEach(
bodyDeclaration ->
bodyDeclaration.ifFieldDeclaration(
fieldDeclaration -> {
NodeList<VariableDeclarator> vars = fieldDeclaration.getVariables();
if (vars.getFirst().isEmpty()) {
// unexpected but just in case.
return;
}
record.addFieldDeclaration(fieldDeclaration);
// Collect uninitialized fields at declaration.
vars.forEach(
variableDeclarator -> {
String fieldName = variableDeclarator.getNameAsString();
if (variableDeclarator.getInitializer().isEmpty()) {
uninitializedFields.put(clazz, fieldName);
}
});
}));
// We still want to keep the information about the class even if it has no field
// declarations, so we can retrieve tha path to the file from the given class flat name.
// This information is used in adding suppression annotations on class level.
return record;
}
ClassFieldRecord record = new ClassFieldRecord(path, clazz);
members.forEach(
bodyDeclaration ->
bodyDeclaration.ifFieldDeclaration(
fieldDeclaration -> {
NodeList<VariableDeclarator> vars = fieldDeclaration.getVariables();
if (vars.getFirst().isEmpty()) {
// unexpected but just in case.
return;
}
record.addFieldDeclaration(fieldDeclaration);
// Collect uninitialized fields at declaration.
vars.forEach(
variableDeclarator -> {
String fieldName = variableDeclarator.getNameAsString();
if (variableDeclarator.getInitializer().isEmpty()) {
uninitializedFields.put(clazz, fieldName);
}
});
}));
// We still want to keep the information about the class even if it has no field
// declarations, so we can retrieve tha path to the file from the given class flat name.
// This information is used in adding suppression annotations on class level.
return record;
};
}

Expand Down Expand Up @@ -194,12 +212,10 @@ public OnField getLocationOnField(String clazz, String field) {
}

/**
* Creates a {@link edu.ucr.cs.riple.injector.location.OnClass} instance targeting the passed
* classes flat name.
* Creates a {@link OnClass} instance targeting the passed classes flat name.
*
* @param clazz Enclosing class of the field.
* @return {@link edu.ucr.cs.riple.injector.location.OnClass} instance targeting the passed
* classes flat name.
* @return {@link OnClass} instance targeting the passed classes flat name.
*/
public OnClass getLocationOnClass(String clazz) {
ClassFieldRecord candidate =
Expand Down
Loading