Skip to content

Commit

Permalink
Fix issues #368, #370, #361 (#371)
Browse files Browse the repository at this point in the history
Fix issue #368 , issue #370 , and issue #361 .
Tests have been added.
One existing test(Issue103) fails, but it was already failing in the
main branch.
  • Loading branch information
gmerr3 authored Nov 7, 2024
1 parent 2259046 commit b26a6de
Show file tree
Hide file tree
Showing 16 changed files with 213 additions and 16 deletions.
13 changes: 13 additions & 0 deletions src/main/java/org/checkerframework/specimin/JavaParserUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.resolution.UnsolvedSymbolException;
import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration;
import com.github.javaparser.resolution.types.ResolvedReferenceType;
import com.github.javaparser.resolution.types.ResolvedType;
import com.google.common.base.Splitter;
Expand Down Expand Up @@ -271,6 +272,18 @@ public static List<String> getReferenceTypesFromCommaSeparatedString(String comm
return types;
}

/**
* Returns a package prefix that can be prepended to a class name, for a given method or
* constructor declaration.
*
* @param decl declaration to extract a package prefix from(using getPackageName)
* @return empty string if default package, otherwise package name followed by "."
*/
public static String packagePrefix(ResolvedMethodLikeDeclaration decl) {
String packageName = decl.getPackageName();
return packageName.isEmpty() ? "" : packageName + ".";
}

/**
* Returns true iff the innermost enclosing class/interface is an enum.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.checkerframework.specimin;

import com.github.javaparser.ParseProblemException;
import com.github.javaparser.ParseResult;
import com.github.javaparser.ParserConfiguration;
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
Expand Down Expand Up @@ -236,7 +237,10 @@ private static void performMinimizationImpl(
Map<String, String> nonPrimaryClassesToPrimaryClass = new HashMap<>();
SourceRoot sourceRoot = new SourceRoot(Path.of(root));
sourceRoot.tryToParse();
for (CompilationUnit compilationUnit : sourceRoot.getCompilationUnits()) {
// getCompilationUnits does not seem to include all files, causing some to be deleted
for (ParseResult<CompilationUnit> res : sourceRoot.getCache()) {
CompilationUnit compilationUnit =
res.getResult().orElseThrow(() -> new RuntimeException(res.getProblems().toString()));
Path pathOfCurrentJavaFile =
compilationUnit.getStorage().get().getPath().toAbsolutePath().normalize();
String primaryTypeQualifiedName = "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ public Visitable visit(ConstructorDeclaration method, Void p) {
targetMethods.add(resolvedMethod.getQualifiedSignature());
unfoundMethods.remove(methodName);
updateUsedClassWithQualifiedClassName(
resolvedMethod.getPackageName() + "." + resolvedMethod.getClassName(),
JavaParserUtil.packagePrefix(resolvedMethod) + resolvedMethod.getClassName(),
usedTypeElements,
nonPrimaryClassesToPrimaryClass);
if (modularityModel.preserveAllFieldsIfTargetIsConstructor()) {
Expand Down Expand Up @@ -341,19 +341,19 @@ public Visitable visit(MethodDeclaration method, Void p) {
if (parentNode instanceof ObjectCreationExpr) {
ObjectCreationExpr parentExpression = (ObjectCreationExpr) parentNode;
ResolvedConstructorDeclaration resolved = parentExpression.resolve();
String methodPackage = resolved.getPackageName();
String methodPackagePrefix = JavaParserUtil.packagePrefix(resolved);
String methodClass = resolved.getClassName();
usedMembers.add(methodPackage + "." + methodClass + "." + method.getNameAsString() + "()");
usedMembers.add(methodPackagePrefix + methodClass + "." + method.getNameAsString() + "()");
updateUsedClassWithQualifiedClassName(
methodPackage + "." + methodClass, usedTypeElements, nonPrimaryClassesToPrimaryClass);
methodPackagePrefix + methodClass, usedTypeElements, nonPrimaryClassesToPrimaryClass);
}
}
String methodWithoutAnySpace = methodName.replaceAll("\\s", "");
if (this.targetMethodNames.contains(methodWithoutAnySpace)) {
ResolvedMethodDeclaration resolvedMethod = method.resolve();
updateUsedClassesForInterface(resolvedMethod);
updateUsedClassWithQualifiedClassName(
resolvedMethod.getPackageName() + "." + resolvedMethod.getClassName(),
JavaParserUtil.packagePrefix(resolvedMethod) + resolvedMethod.getClassName(),
usedTypeElements,
nonPrimaryClassesToPrimaryClass);

Expand Down Expand Up @@ -518,9 +518,11 @@ public Visitable visit(MethodCallExpr call, Void p) {
resolvedYetStuckMethodCall.add(scopeAsString + "." + call.getNameAsString());
usedTypeElements.add(scopeAsString);
} else {
String packagePrefix =
getCurrentPackage().isEmpty() ? "" : getCurrentPackage() + ".";
resolvedYetStuckMethodCall.add(
getCurrentPackage() + "." + scopeAsString + "." + call.getNameAsString());
usedTypeElements.add(getCurrentPackage() + "." + scopeAsString);
packagePrefix + scopeAsString + "." + call.getNameAsString());
usedTypeElements.add(packagePrefix + scopeAsString);
}
}
}
Expand Down Expand Up @@ -564,7 +566,7 @@ public Visitable visit(MethodCallExpr call, Void p) {
private void preserveMethodDecl(ResolvedMethodDeclaration decl) {
usedMembers.add(decl.getQualifiedSignature());
updateUsedClassWithQualifiedClassName(
decl.getPackageName() + "." + decl.getClassName(),
JavaParserUtil.packagePrefix(decl) + decl.getClassName(),
usedTypeElements,
nonPrimaryClassesToPrimaryClass);
try {
Expand Down Expand Up @@ -624,7 +626,7 @@ public Visitable visit(ObjectCreationExpr newExpr, Void p) {
ResolvedConstructorDeclaration resolved = newExpr.resolve();
usedMembers.add(resolved.getQualifiedSignature());
updateUsedClassWithQualifiedClassName(
resolved.getPackageName() + "." + resolved.getClassName(),
JavaParserUtil.packagePrefix(resolved) + resolved.getClassName(),
usedTypeElements,
nonPrimaryClassesToPrimaryClass);
for (int i = 0; i < resolved.getNumberOfParams(); ++i) {
Expand All @@ -646,7 +648,7 @@ public Visitable visit(ExplicitConstructorInvocationStmt expr, Void p) {
ResolvedConstructorDeclaration resolved = expr.resolve();
usedMembers.add(resolved.getQualifiedSignature());
updateUsedClassWithQualifiedClassName(
resolved.getPackageName() + "." + resolved.getClassName(),
JavaParserUtil.packagePrefix(resolved) + resolved.getClassName(),
usedTypeElements,
nonPrimaryClassesToPrimaryClass);
}
Expand Down Expand Up @@ -850,10 +852,6 @@ public static void updateUsedClassWithQualifiedClassName(
Set<String> usedTypeElement,
Map<String, String> nonPrimaryClassesToPrimaryClass) {

// in case of type variables
if (!qualifiedClassName.contains(".")) {
return;
}
// strip type variables, if they're present
if (qualifiedClassName.contains("<")) {
qualifiedClassName = qualifiedClassName.substring(0, qualifiedClassName.indexOf("<"));
Expand All @@ -867,6 +865,10 @@ public static void updateUsedClassWithQualifiedClassName(
nonPrimaryClassesToPrimaryClass);
}

// in case of type variables TODO:investigate side effects of having moved this from earlier
if (!qualifiedClassName.contains(".")) {
return;
}
String potentialOuterClass =
qualifiedClassName.substring(0, qualifiedClassName.lastIndexOf("."));
if (JavaParserUtil.isAClassPath(potentialOuterClass)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,9 @@ public boolean updatedAddedTargetFilesForPotentialEnum(FieldAccessExpr expr) {
return false;
}
if (resolved.isEnumConstant()) {
if (JavaLangUtils.inJdkPackage(resolved.getType().describe())) {
return false;
}
String filePathName = qualifiedNameToFilePath(resolved.getType().describe());
if (!addedTargetFiles.contains(filePathName)) {
gotException();
Expand Down Expand Up @@ -3257,7 +3260,8 @@ public static UnsolvedClassOrInterface getSimpleSyntheticClassFromFullyQualified
+ fullyName);
}
String className = fullyQualifiedToSimple(fullyName);
String packageName = fullyName.replace("." + className, "");
String packageName =
fullyName.contains("." + className) ? fullyName.replace("." + className, "") : "";
return new UnsolvedClassOrInterface(className, packageName);
}

Expand Down
15 changes: 15 additions & 0 deletions src/test/java/org/checkerframework/specimin/BuiltinEnumTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.checkerframework.specimin;

import java.io.IOException;
import org.junit.Test;

/** This test checks if Specimin is able to process Java builtin enum classes. Issue #361 */
public class BuiltinEnumTest {
@Test
public void runTest() throws IOException {
SpeciminTestExecutor.runTestWithoutJarPaths(
"enumbuiltin",
new String[] {"com/example/Scheduler.java"},
new String[] {"com.example.Scheduler#schedule(Runnable, int)"});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.checkerframework.specimin;

import java.io.IOException;
import org.junit.Test;

/**
* This test checks if Specimin correctly preserves things that are used as arguments to enum
* constants.
*/
public class DefaultPackageTest {
@Test
public void runTest() throws IOException {
SpeciminTestExecutor.runTestWithoutJarPaths(
"defaultpackage", new String[] {"A.java"}, new String[] {"A#schedule(Runnable, int)"});
}
}
32 changes: 32 additions & 0 deletions src/test/java/org/checkerframework/specimin/Issue368Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.checkerframework.specimin;

import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import org.junit.Test;

/**
* This test is the test described in <a
* href="https://github.com/njit-jerse/specimin/issues/368">...</a>. The issue caused the original
* source file A.java to be deleted, and in addition not appear in the output.
*/
public class Issue368Test {
@Test
public void runTest() throws IOException {
Path fileASource = Path.of("src/test/resources/issue368/A_code.java");
Path fileAPath = Path.of("src/test/resources/issue368/input/com/example/A.java");
Files.copy(fileASource, fileAPath, StandardCopyOption.REPLACE_EXISTING);
String fileACode = Files.readString(fileAPath);
SpeciminTestExecutor.runTestWithoutJarPaths(
"issue368", new String[] {"com/example/B.java"}, new String[] {"com.example.B#test()"});

try {
assertTrue(Files.exists(fileAPath));
} finally {
Files.writeString(fileAPath, fileACode);
}
}
}
6 changes: 6 additions & 0 deletions src/test/resources/defaultpackage/expected/A.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
public class A {

public static void schedule(Runnable task, int millis) {
System.out.println();
}
}
5 changes: 5 additions & 0 deletions src/test/resources/defaultpackage/input/A.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public class A{//no package declaration
public static void schedule(Runnable task, int millis) {
System.out.println();
}
}
13 changes: 13 additions & 0 deletions src/test/resources/enumbuiltin/expected/com/example/Scheduler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Scheduler {

public static final ScheduledExecutorService ses = null;

public static void schedule(Runnable task, int millis) {
ses.schedule(task, millis, TimeUnit.MILLISECONDS);
}
}
14 changes: 14 additions & 0 deletions src/test/resources/enumbuiltin/input/com/example/Scheduler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Scheduler{
private Scheduler() {}
public static final ScheduledExecutorService ses=newSingleThreadScheduledExecutor(r->new Thread(r,"Scheduler thread"));

public static void schedule(Runnable task, int millis) {
ses.schedule(task,millis,TimeUnit.MILLISECONDS);
}
}
21 changes: 21 additions & 0 deletions src/test/resources/issue368/A_code.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.example;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousSocketChannel;

public class A{
public AsynchronousSocketChannel client;
public A(AsynchronousSocketChannel client){
this.client=client;
}
public InetAddress test(){//target method
try {
if(client.getRemoteAddress() instanceof InetSocketAddress isa)
return isa.getAddress();
} catch (IOException e) {

}
return null;
}
}
10 changes: 10 additions & 0 deletions src/test/resources/issue368/expected/com/example/A.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example;

import java.net.InetAddress;

public class A {

public InetAddress test() {
throw new Error();
}
}
10 changes: 10 additions & 0 deletions src/test/resources/issue368/expected/com/example/B.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example;

import java.net.InetAddress;

public class B extends A {

public InetAddress test() {
return super.test();
}
}
21 changes: 21 additions & 0 deletions src/test/resources/issue368/input/com/example/A.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.example;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousSocketChannel;

public class A{
public AsynchronousSocketChannel client;
public A(AsynchronousSocketChannel client){
this.client=client;
}
public InetAddress test(){//target method
try {
if(client.getRemoteAddress() instanceof InetSocketAddress isa)
return isa.getAddress();
} catch (IOException e) {

}
return null;
}
}
11 changes: 11 additions & 0 deletions src/test/resources/issue368/input/com/example/B.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example;
import java.io.IOException;
import java.net.InetAddress;

public class B extends A {

@Override
public InetAddress test(){
return super.test();
}
}

0 comments on commit b26a6de

Please sign in to comment.