Skip to content

Handle internal function signatures correctly for functions used by records in Java 17 #513

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

Open
wants to merge 6 commits into
base: java-17
Choose a base branch
from

Conversation

eklaDFF
Copy link

@eklaDFF eklaDFF commented Jan 30, 2025

No description provided.

@cyrille-artho
Copy link
Member

It looks like the tests for equals and hashcode still fail:

java17.RecordFeatureTest > testRecordHashCode FAILED
    java.lang.AssertionError at RecordFeatureTest.java:38

java17.RecordFeatureTest > testRecordEquality FAILED
    java.lang.AssertionError at RecordFeatureTest.java:28

Please check.

@eklaDFF
Copy link
Author

eklaDFF commented Feb 1, 2025 via email

@cyrille-artho
Copy link
Member

cyrille-artho commented Feb 3, 2025

  functionalInterfaceName = "java.lang.Boolean";

Perhaps the Java bytecode-style class descriptor is needed here? That would be Ljava/lang/Boolean. Try that and see if the test for equals then passes.

@eklaDFF
Copy link
Author

eklaDFF commented Feb 5, 2025 via email

@eklaDFF
Copy link
Author

eklaDFF commented Feb 10, 2025 via email

@cyrille-artho
Copy link
Member

Try Ljava/lang/Boolean instead of Ljava.lang.Boolean, as forward slashes are used in the bytecode.

@eklaDFF
Copy link
Author

eklaDFF commented Feb 10, 2025 via email

@eklaDFF
Copy link
Author

eklaDFF commented Feb 10, 2025 via email

@cyrille-artho
Copy link
Member

Your current version in the PR does not translate I to Ljava/lang/Integer. If you would like us to see what happens in that case, please update the code in your branch (eklaDFF:PrimitiveClassesBranch) so we can see the exact same code that you have and run the CI build to see the outcome of the tests.

@eklaDFF
Copy link
Author

eklaDFF commented Feb 13, 2025

Hi, updated the one statement as

if (Objects.equals(functionalInterfaceName,"Z")){
          functionalInterfaceName = "java/lang/Boolean";
        }else if (Objects.equals(functionalInterfaceName,"I")){
          functionalInterfaceName = "Ljava/lang/Integer";
        }

to make difference in their output behaviour.

@cyrille-artho
Copy link
Member

The test still fail, and I'm not sure why. The initial "L" is missing in "Ljava/lang/Boolean", but the descriptor for Integer is correct, so that is not necessarily the reason for the test for "equals" to fail.
Try adding the "L" anyway to see if the test for "equals" then passes.

@eklaDFF
Copy link
Author

eklaDFF commented Feb 14, 2025

Actually I left them intentionally to see how they behave. But fixed them now. Now look at them.

Should I debug them and create a diagram of program flow to understand ? Or should schedule a meeting for exploring what is happening there, if you have time ?

One thing to notice is that when we remove L, it do not throws ClassNotFoundException. What is the reason ?

@eklaDFF
Copy link
Author

eklaDFF commented Feb 14, 2025

Overall, ClassLoader is having some problem ? Like not not loading the Class at the time of requirement ?

@cyrille-artho
Copy link
Member

Actually I left them intentionally to see how they behave. But fixed them now. Now look at them.

Should I debug them and create a diagram of program flow to understand ? Or should schedule a meeting for exploring what is happening there, if you have time ?

One thing to notice is that when we remove L, it do not throws ClassNotFoundException. What is the reason ?

That is interesting; perhaps some parts of JPF's code expect that the L is removed earlier.
I recommend checking a different case, where an invocation succeeds (in the existing code base). Just by inspecting the content of the interface or class name there, we can see what the expected format is. That will allow us to use the right string.

@eklaDFF
Copy link
Author

eklaDFF commented Feb 20, 2025

You were right,

.....
functionalInterfaceName=Ljava/util/function/Function;
functionalInterfaceName=Ljava/util/function/Function;
functionalInterfaceName=Ljava/util/function/Function;
functionalInterfaceName=Ljava/util/function/BiFunction;
functionalInterfaceName=Ljava/lang/String;
functionalInterfaceName=I
functionalInterfaceName=Z

====================================================== error 1
gov.nasa.jpf.vm.NoUncaughtExceptionsProperty
java.lang.ClassNotFoundException: class not found: Ljava.lang.Integer
	at java17.RecordFeatureTest$Point.hashCode(RecordFeatureTest.java:7)
	at java17.RecordFeatureTest.testRecordHashCode(RecordFeatureTest.java:42)
	at java.lang.reflect.Method.invoke(gov.nasa.jpf.vm.JPF_java_lang_reflect_Method)
	at gov.nasa.jpf.util.test.TestJPF.runTestMethod(TestJPF.java:648)
.....

From here, we are assured that either we have to move forward with directly I,Z or their corresponding full class name ?

@cyrille-artho
Copy link
Member

Try a workaround in Method.invoke. Probably, replacing all . with / will do the trick. The easiest way to find out is to try it out.

@cyrille-artho cyrille-artho changed the title Primitive classes branch Handle internal function signatures correctly for functions used by records in Java 17 Feb 21, 2025
@eklaDFF
Copy link
Author

eklaDFF commented Feb 23, 2025

Either it is . or /, it will be changed to .. Actually value of functionalInterfaceName is changed later by replacing / with . and this works for other cases. So this idea may be not a right direction.

@cyrille-artho
Copy link
Member

Thanks for checking. It must be something else then.

@eklaDFF
Copy link
Author

eklaDFF commented Feb 23, 2025 via email

@eklaDFF
Copy link
Author

eklaDFF commented Feb 26, 2025 via email

@cyrille-artho
Copy link
Member

Thanks. It looks like the failed class lookup in your video happens at application level, not within JPF. JPF reports a violation of "NoUncaughtExceptionProperty" with a stack trace that includes (only) the test case.
Perhaps the problem lies in the boxing/unboxing?
What happens if you have a simpler test for the hash code first, one that just checks if the hash code is not 0, or one that prints it? Perhaps there is a way to avoid the boxing/unboxing with a simpler test, so we can first check that the hash code works correctly and then address the boxing part.

@eklaDFF
Copy link
Author

eklaDFF commented Feb 28, 2025

@Test
    public void testPrintingRecordHashCode(){
        if(verifyNoPropertyViolation()){
            Point point = new Point(4,5);
            System.out.println(point.hashCode());
        }
    }

Still the problem remains same as

====================================================== error 1
gov.nasa.jpf.vm.NoUncaughtExceptionsProperty
java.lang.ClassNotFoundException: class not found: Ljava.lang.Integer
	at java17.RecordFeatureTest$Point.hashCode(RecordFeatureTest.java:7)
	at java17.RecordFeatureTest.testPrintingRecordHashCode(RecordFeatureTest.java:50)
	at java.lang.reflect.Method.invoke(gov.nasa.jpf.vm.JPF_java_lang_reflect_Method)
	at gov.nasa.jpf.util.test.TestJPF.runTestMethod(TestJPF.java:648)

@cyrille-artho
Copy link
Member

It is possible that something that goes wrong at the JPF level causes the exception to appear at application level.
Try to look in jpf-core at what point the type descriptor is wrong and cannot be found.

@eklaDFF
Copy link
Author

eklaDFF commented Mar 2, 2025

IMPORTANT Point 1 :

if (Objects.equals(functionalInterfaceName,"Z")){
          functionalInterfaceName = "java/lang/Boolean";
        }else if (Objects.equals(functionalInterfaceName,"I")){
          functionalInterfaceName = "java/lang/Integer";
        }

When we pass functionalInterfaceName with class descriptor, testPrintingRecordHashCode()(very simple Test case) passes.

@eklaDFF
Copy link
Author

eklaDFF commented Mar 2, 2025

Is there any other way to see the flow of method calls with parameters ? Printing Statements creates huge logs and becomes complex to analyse them.

@cyrille-artho
Copy link
Member

You can try adding the option +listener=gov.nasa.jpf.listener.MethodTracker (or even ExecTracker if you want to see every single instruction).

@eklaDFF
Copy link
Author

eklaDFF commented Mar 13, 2025

To see me method flow, added listener = gov.nasa.jpf.listener.MethodTracker in jpf.properties.
And below is the output (last few portion of output) : [Only for testRecordHashCode()]

ClassInfo ciTop = gov.nasa.jpf.util.test.TestJPF
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java.lang.Object
0:      native java.lang.reflect.Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
0:      native java.lang.reflect.Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
Method.invoke() >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
MethodInfo miCallee = MethodInfo[java17.RecordFeatureTest.testRecordHashCode()V]
ClassInfo calleeClass = ClassInfo[name=java17.RecordFeatureTest]
DirectCallStackFrame frame = null ? true
miCallee is not static = true
objRef == MJIEnv.NULL ? false
Here
ElementInfo eiObj = null ? false
ClassInfo objClass = java17.RecordFeatureTest
Here3
Here4
Here5
0:        java17.RecordFeatureTest.[testRecordHashCode]
0:          java17.RecordFeatureTest.testRecordHashCode()V
ThreadInfo.resolveReferencedClass() called >>>>>>>>>>>>>>> Parameter -> String clsName = java.lang.String
ClassInfo ciTop = java17.RecordFeatureTest
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java.lang.String
0:            native gov.nasa.jpf.util.test.TestJPF.verifyNoPropertyViolation([Ljava/lang/String;)Z
0:            native gov.nasa.jpf.util.test.TestJPF.verifyNoPropertyViolation([Ljava/lang/String;)Z
0:          java17.RecordFeatureTest.testRecordHashCode()V
ThreadInfo.resolveReferencedClass() called >>>>>>>>>>>>>>> Parameter -> String clsName = java17.RecordFeatureTest$Point
ClassInfo ciTop = java17.RecordFeatureTest
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java17.RecordFeatureTest$Point
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java/lang/Object
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java/lang/Record
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java/lang/Object
0:            java17.RecordFeatureTest$Point.<init>(II)V
0:              java.lang.Record.<init>()V
0:                java.lang.Object.<init>()V
0:              java.lang.Record.<init>()V
0:            java17.RecordFeatureTest$Point.<init>(II)V
0:          java17.RecordFeatureTest.testRecordHashCode()V
ThreadInfo.resolveReferencedClass() called >>>>>>>>>>>>>>> Parameter -> String clsName = java17.RecordFeatureTest$Point
ClassInfo ciTop = java17.RecordFeatureTest
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java17.RecordFeatureTest$Point
0:            java17.RecordFeatureTest$Point.<init>(II)V
0:              java.lang.Record.<init>()V
0:                java.lang.Object.<init>()V
0:              java.lang.Record.<init>()V
0:            java17.RecordFeatureTest$Point.<init>(II)V
0:          java17.RecordFeatureTest.testRecordHashCode()V
ThreadInfo.resolveReferencedClass() called >>>>>>>>>>>>>>> Parameter -> String clsName = java17.RecordFeatureTest$Point
ClassInfo ciTop = java17.RecordFeatureTest
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java17.RecordFeatureTest$Point
0:            java17.RecordFeatureTest$Point.<init>(II)V
0:              java.lang.Record.<init>()V
0:                java.lang.Object.<init>()V
0:              java.lang.Record.<init>()V
0:            java17.RecordFeatureTest$Point.<init>(II)V
0:          java17.RecordFeatureTest.testRecordHashCode()V
0:            java17.RecordFeatureTest$Point.hashCode()I
INVOKEDYNAMIC.execute() called>>>>>
ElementInfo ei == null ? true
functionalInterfaceName = Ljava/lang/Integer
ThreadInfo.resolveReferencedClass() called >>>>>>>>>>>>>>> Parameter -> String clsName = Ljava/lang/Integer
ClassInfo ciTop = java17.RecordFeatureTest$Point
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = Ljava/lang/Integer
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java/lang/ReflectiveOperationException
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java/lang/Exception
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java/lang/Throwable
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java/lang/Object
0:              java.lang.ClassNotFoundException.[<clinit>]
0:                java.lang.ClassNotFoundException.<clinit>()V
ThreadInfo.resolveReferencedClass() called >>>>>>>>>>>>>>> Parameter -> String clsName = java.io.ObjectStreamField
ClassInfo ciTop = java.lang.ClassNotFoundException
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java.io.ObjectStreamField
ThreadInfo.resolveReferencedClass() called >>>>>>>>>>>>>>> Parameter -> String clsName = java.io.ObjectStreamField
ClassInfo ciTop = java.lang.ClassNotFoundException
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java.io.ObjectStreamField
ThreadInfo.resolveReferencedClass() called >>>>>>>>>>>>>>> Parameter -> String clsName = java.lang.Throwable
ClassInfo ciTop = java.lang.ClassNotFoundException
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java.lang.Throwable
0:                  java.io.ObjectStreamField.<init>(Ljava/lang/String;Ljava/lang/Class;)V
0:                    java.io.ObjectStreamField.<init>(Ljava/lang/String;Ljava/lang/Class;Z)V
0:                      java.lang.Object.<init>()V
0:                    java.io.ObjectStreamField.<init>(Ljava/lang/String;Ljava/lang/Class;Z)V
0:                  java.io.ObjectStreamField.<init>(Ljava/lang/String;Ljava/lang/Class;)V
0:                java.lang.ClassNotFoundException.<clinit>()V
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java.lang.ClassNotFoundException
0:              java.lang.ClassNotFoundException.[<clinit>]
ClassInfo.resolveReferencedClass() called >>>>>>>>>> Parameter -> String cname = java/lang/Exception
0:    gov.nasa.jpf.util.test.TestJPF.runTestMethod([Ljava/lang/String;)V
0:      java.lang.Throwable.getCause()Ljava/lang/Throwable;
0:    gov.nasa.jpf.util.test.TestJPF.runTestMethod([Ljava/lang/String;)V
----------------------------------- [1] forward: 0 new

====================================================== error 1
gov.nasa.jpf.vm.NoUncaughtExceptionsProperty
java.lang.ClassNotFoundException: class not found: Ljava.lang.Integer
	at java17.RecordFeatureTest$Point.hashCode(RecordFeatureTest.java:7)
	at java17.RecordFeatureTest.testRecordHashCode(RecordFeatureTest.java:42)
	at java.lang.reflect.Method.invoke(gov.nasa.jpf.vm.JPF_java_lang_reflect_Method)
	at gov.nasa.jpf.util.test.TestJPF.runTestMethod(TestJPF.java:648)


====================================================== snapshot #1
thread java.lang.Thread:{id:0,name:main,status:RUNNING,priority:5,isDaemon:false,lockCount:0,suspendCount:0}
  call stack:
	at gov.nasa.jpf.util.test.TestJPF.runTestMethod(TestJPF.java:650)

----------------------------------- search finished

====================================================== results
error #1: gov.nasa.jpf.vm.NoUncaughtExceptionsProperty "java.lang.ClassNotFoundException: class not found:..."

====================================================== search finished: 13/03/25, 6:33 pm

java.lang.AssertionError: JPF found unexpected errors: gov.nasa.jpf.vm.NoUncaughtExceptionsProperty
	at gov.nasa.jpf.util.test.TestJPF.fail(TestJPF.java:164)
	at gov.nasa.jpf.util.test.TestJPF.noPropertyViolation(TestJPF.java:816)
	at gov.nasa.jpf.util.test.TestJPF.verifyNoPropertyViolation(TestJPF.java:830)
	at java17.RecordFeatureTest.testRecordHashCode(RecordFeatureTest.java:38)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:569)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:112)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:40)
	at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:60)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:52)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:569)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at jdk.proxy1/jdk.proxy1.$Proxy2.processTestClass(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)


> Task :test FAILED
java17.RecordFeatureTest > testRecordHashCode FAILED
    java.lang.AssertionError at RecordFeatureTest.java:38
Test Execution: FAILURE
Summary: 1 tests, 0 passed, 1 failed, 0 skipped
1 test completed, 1 failed
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///Users/ekla/GSOC/JPFjava17/jpf-core/build/reports/tests/test/index.html
BUILD FAILED in 2s
18 actionable tasks: 5 executed, 13 up-to-date
  1. Please have a look from your side also.
  2. One thing I have noticed in above output that there is return types using I, Z,etc. If this is correct, (and it seems correct), translating the functionalInterfaceName name to corresponding class type description does not matters.
  3. If the problem is related to class initialization, then should i insert a statement inside the testRecordHashCode() method just after verifyNoPropertyViolation(). This might helps us to differentiate the output in two section and then we can see whether the I or Ljava.lang.Integer is initialized. Do you think this approach will work ?

@cyrille-artho cyrille-artho mentioned this pull request Mar 14, 2025
@cyrille-artho
Copy link
Member

@pparizek , can you please check?
One thing I notice is that parts of the log show the full internal class name (Lx/y/z;), others show the source-level class name x.y.z, and then, we also have the mixed variant, x/y/z (without the preceding L and trailing ;). Perhaps x/y/z should be either Lx/y/z; or then x.y.z? It is also possible that JPF handles shorthands for primitive types (I, Z, etc.) correctly for return types but not for function arguments, or vice versa.

@pparizek
Copy link
Contributor

There are some debugging print statements in some of the commits, that should not be merged into the java-17 branch. Please remove them first.

@pparizek
Copy link
Contributor

I will have more time to dig into this (read the log/output, think about internal class names versus source-level class name) starting from April 7 or so. At this time we have a local grant application deadline approaching very fat, and now I am very busy with teaching-related stuff too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants