diff --git a/.github/workflows/simple_build.yml b/.github/workflows/simple_build.yml index 89dd792a..f563ab0a 100644 --- a/.github/workflows/simple_build.yml +++ b/.github/workflows/simple_build.yml @@ -27,3 +27,5 @@ jobs: run: ./gradlew check - name: Run PolDet test run: ./gradlew testPolDet + - name: Test on modified class file + run: ./gradlew runModifiedTest --info diff --git a/.gitignore b/.gitignore index 390662a1..c3c891e9 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,12 @@ tmp/ .classpath .project +####### Ignoring VS Code settings ####### +.vscode/ +settings.json +.log +.temp + ####### Covers JetBrains IDE: IntelliJ ####### *.iml *.ipr @@ -82,3 +88,6 @@ local temp.ser output.txt file + +#json file +.vscode/settings.json diff --git a/build.gradle b/build.gradle index f7bdc0f0..5a433968 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,5 @@ +import java.nio.file.Files +import java.nio.file.Paths plugins { id 'java' id 'maven-publish' @@ -29,6 +31,15 @@ repositories { dependencies { testImplementation 'org.ow2.asm:asm:9.5' testImplementation('junit:junit:4.13.1') + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2' +} +sourceSets { + main { + java { + srcDirs=['src/main'] + } + } } configurations { @@ -57,6 +68,39 @@ apply from: "gradle/build-resources.gradle" compileJava.dependsOn(copyLibs) +tasks.register("modifyAndCompileD") { + // Declaring inputs and outputs to make the task compatible with the configuration cache + inputs.file("src/tests/gov/nasa/jpf/test/D.java") + outputs.dir("${buildDir}/classes/test/java") + + doLast { + // Step 1: Defining file paths + def originalFilePath = "src/tests/gov/nasa/jpf/test/D.java" + def originalFile = Paths.get(originalFilePath) + def tempDir = file("${buildDir}/tempD") + def buildClassesDir = file("${buildDir}/classes/java/test") + + // Step 2: Creating a temporary directory + tempDir.mkdirs() + + def content = Files.readString(originalFile) + + content = content.replace("static ", "") + + def modifiedFile = file("${tempDir}/D.java") + + Files.writeString(modifiedFile.toPath(), content) + + def javac = org.gradle.internal.jvm.Jvm.current().javacExecutable + exec { + commandLine javac, "-d", buildClassesDir, modifiedFile + } + + println "Modified and recompiled D.java without 'static'." + } +} + + tasks.named('compileClassesJava') { dependsOn(copyLibs) dependsOn(copyResources) @@ -73,6 +117,21 @@ tasks.named('compilePeersJava') { dependsOn(copyLibs) dependsOn(copyResources) } +task runModifiedTest(type: Test) { + description = 'Run modified test' + group = 'verification' + testClassesDirs = sourceSets.test.output.classesDirs + classpath = sourceSets.test.runtimeClasspath + include '**/StaticCallToNonStaticTest.class' +} + +tasks.named("runModifiedTest") { + dependsOn("compileTestJava") + shouldRunAfter("test") +} +tasks.named('modifyAndCompileD') { + shouldRunAfter('compileTestJava') +} tasks.named('compileTestJava') { dependsOn(copyLibs) dependsOn(copyResources) @@ -81,11 +140,19 @@ tasks.named('jar') { dependsOn(copyLibs) dependsOn(copyResources) } +tasks.named("test") { + dependsOn(tasks.named("modifyAndCompileD")) +} +tasks.named('modifyAndCompileD') { + dependsOn('compileTestJava') +} + tasks.register('compileModules', JavaCompile) { dependsOn(copyLibs) dependsOn compileTestJava + dependsOn modifyAndCompileD source = fileTree(dir: 'src/classes/modules') classpath = files('build/annotations', 'build/classes', 'build/main') options.compilerArgs = [ @@ -121,6 +188,7 @@ tasks.register('createJpfClassesJar', Jar) { dependsOn(copyLibs) dependsOn compile dependsOn copyResources + dependsOn modifyAndCompileD from sourceSets.classes.java.destinationDirectory from sourceSets.annotations.java.destinationDirectory @@ -281,6 +349,7 @@ test { maxHeapSize = "1024m" include "**/*Test.class" + include "**/*StaticCallToNonStaticTest.class" exclude "**/SplitInputStreamTest.class" exclude "**/JPF_*.class" @@ -302,6 +371,7 @@ test { println "Summary: " + summaryFields.join(", ") } } + useJUnitPlatform() } jacoco { @@ -364,4 +434,4 @@ tasks.register('runPolDet', Exec) { commandLine 'java', '-jar', "${buildDir}/RunJPF.jar", "+classpath=${JPFClasspath}", "PolDetMain", project.findProperty("testClass") ?: "" } -defaultTasks "buildJars" +defaultTasks "buildJars" \ No newline at end of file diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/INVOKESTATIC.java b/src/main/gov/nasa/jpf/jvm/bytecode/INVOKESTATIC.java index 0ff67ae0..90fe57ff 100644 --- a/src/main/gov/nasa/jpf/jvm/bytecode/INVOKESTATIC.java +++ b/src/main/gov/nasa/jpf/jvm/bytecode/INVOKESTATIC.java @@ -18,6 +18,8 @@ package gov.nasa.jpf.jvm.bytecode; +import java.lang.reflect.Modifier; + import gov.nasa.jpf.vm.ClassInfo; import gov.nasa.jpf.vm.ClassLoaderInfo; import gov.nasa.jpf.vm.ElementInfo; @@ -89,6 +91,9 @@ public Instruction execute (ThreadInfo ti) { return ti.createAndThrowException("java.lang.NoSuchMethodException", cname + '.' + mname); } + if(!callee.isStatic()) { + return ti.createAndThrowException("java.lang.IncompatibleClassChangeError", callee.getFullName()); + } // this can be actually different than (can be a base) ClassInfo ciCallee = callee.getClassInfo(); diff --git a/src/tests/gov/nasa/jpf/StaticCallToNonStaticTest.java b/src/tests/gov/nasa/jpf/StaticCallToNonStaticTest.java new file mode 100644 index 00000000..450a6f9f --- /dev/null +++ b/src/tests/gov/nasa/jpf/StaticCallToNonStaticTest.java @@ -0,0 +1,14 @@ +package gov.nasa.jpf; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; + +class StaticCallToNonStaticTest { + + @Test + void testStaticCallToNonStatic() { + Assertions.assertThrows(IncompatibleClassChangeError.class, () -> { + D.m(); + }); + } +} diff --git a/src/tests/gov/nasa/jpf/test/D.java b/src/tests/gov/nasa/jpf/test/D.java new file mode 100644 index 00000000..dad25625 --- /dev/null +++ b/src/tests/gov/nasa/jpf/test/D.java @@ -0,0 +1,9 @@ +// src/tests/gov/nasa/jpf/test/D.java +package gov.nasa.jpf; + +public class D { + // Non-static method + public void m() { + System.out.println("Non-static method m() called"); + } +}