javapathfinder / jpf-core

JPF is an extensible software analysis framework for Java bytecode. jpf-core is the basis for all JPF projects; you always need to install it. It contains the basic VM and model checking infrastructure, and can be used to check for concurrency defects like deadlocks, and unhandled exceptions like NullPointerExceptions and AssertionErrors.
517 stars 326 forks source link

`BufferTest.testCharBufferConstructor` uses random bytes #459

Open cyrille-artho opened 1 month ago

cyrille-artho commented 1 month ago

The String constructor with an array of bytes as an argument requires a correctly encoded string. Quoting from the API docs: "Constructs a new String by decoding the specified array of bytes using the platform's default charset. [...] The behavior of this constructor when the given bytes are not valid in the default charset is unspecified."

We want to replace the random bytes with two examples, one using UTF-8 and the other one Latin-1. We want to be sure that (a) non-ASCII characters are covered in each test, (b) the complete test string is fully encoded with only valid bytes within the given test array. How to get example strings: Write a small Java program with two example strings, print them as hexadecimal, run that in the regular JVM. (a) at least one multi-byte character in UTF-8; (b) at least one two-byte non-ASCII character from the Latin-1 set.

eklaDFF commented 1 month ago

Among three issues, we have to first deal with it to go in chronological order ?

456

458

459

eklaDFF commented 1 month ago

Created example test cases :

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

public class Main {
    public static void main(String[] args){

            String utf8String = "Hello, 世界";

        String latin1String = "Hello, Åland";

        byte[] utf8Bytes = utf8String.getBytes(StandardCharsets.UTF_8);
        System.out.println("UTF-8 Hexadecimal:");
            printHexadecimal(utf8Bytes);

            byte[] latin1Bytes = latin1String.getBytes(StandardCharsets.ISO_8859_1);
            System.out.println("Latin-1 Hexadecimal:");
            printHexadecimal(latin1Bytes);
    }

    private static void printHexadecimal(byte[] bytes) {
            for (byte b : bytes) {
                System.out.printf("%02X ", b);
            }
            System.out.println();
        }
}

Here is the output :

UTF-8 Hexadecimal:
48 65 6C 6C 6F 2C 20 E4 B8 96 E7 95 8C 
Latin-1 Hexadecimal:
48 65 6C 6C 6F 2C 20 C5 6C 61 6E 64 

@cyrille-artho

cyrille-artho commented 4 weeks ago

Thanks, I think issue #459 is the best one to try first. Regarding your example, look into how the second string (Åland) gets encoded in UTF-8. This will help us see why one of the tests now fails due to (perhaps) conversions of a string from one format to another one. The first string (世界) cannot be encoded in Latin1, so the problem will not appear.

eklaDFF commented 3 weeks ago

Now what I should do? How do we have to apply these test cases ?

Do we have to create two different test units ? If so, whats the process ? BufferTest.class is read-only.

cyrille-artho commented 3 weeks ago

First, create a test that forces the second string to be encoded as UTF-8, so we can see the difference. After that, you can run the test from BufferTest in the regular JVM to see what the expected result under Java 17 is. From this, you can derive the test input for JPF and adapt the test case testCharBufferConstructor in BufferTest.java, so we have a meaningful byte array as input.

eklaDFF commented 3 weeks ago
Screenshot 2024-06-11 at 12 12 17 AM

Is this you referring about ?

cyrille-artho commented 3 weeks ago

Yes, this looks great! As the next step, please try to enhance testCharBufferConstructor by having a few tests with these byte arrays as inputs to Scanner. I think all three inputs are useful:

  1. 世界 only works in UTF-8; if you pass the same byte array trying to parse it as Latin-1, we get a different string (with some "gobbledygook" for the last few characters).
  2. The second example works when parsed as Latin-1 but will fail as UTF-8 and produce an exception.
  3. The third examples works either way but will produce the wrong string (Â followed by a non-printable character instead of Å).

So we want to try six uses of the scanner: Three that work as intended, two that produce the wrong string (cases 1 and 3 with the wrong encoding applied), and one that throws an exception (case 2 with the wrong encoding). (My prediction might be wrong because sometimes, a Latin-1 string can be decoded as UTF-8; you will see what happens if you run the code on the JVM).

eklaDFF commented 3 weeks ago

testCharBufferConstructor is read only. How do I create/modify them and run them to test with six different String as you said above ?

cyrille-artho commented 3 weeks ago

What do you mean by "read-only"? The file has permission 0600 (rw for the owner), and if you run your editor with the same user ID as the one that you use with git, you can edit the file. Other test code has the same permissions. Please check your setup, the source files should all be editable.

eklaDFF commented 3 weeks ago

@cyrille-artho Here is the result from JVM

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class Main {
    public static void main(String[] args){
        String utf8String = "Hello, 世界";

        String latin1String = "Hello, Åland";

        byte[] utf8Bytes = utf8String.getBytes(StandardCharsets.UTF_8);
        System.out.println("UTF-8 Hexadecimal:");
            printHexadecimal(utf8Bytes);

            byte[] latin1Bytes = latin1String.getBytes(StandardCharsets.ISO_8859_1);
            System.out.println("Latin-1 Hexadecimal:");
            printHexadecimal(latin1Bytes);

            byte[] utf8Bytes2 = latin1String.getBytes(StandardCharsets.UTF_8);
            System.out.println("UTF-8 Hexadecimal:");
            printHexadecimal(utf8Bytes2);

            // Decodings...........

            Scanner scanner = new Scanner(new String(utf8Bytes,StandardCharsets.UTF_8));
            while (scanner.hasNext()){
                System.out.println(scanner.nextLine());
            }

            Scanner scanner2 = new Scanner(new String(latin1Bytes,StandardCharsets.UTF_8));
            while (scanner2.hasNext()){
                System.out.println(scanner2.nextLine());
            }

            Scanner scanner3 = new Scanner(new String(utf8Bytes2,StandardCharsets.UTF_8));
            while (scanner3.hasNext()){
                System.out.println(scanner3.nextLine());
            }

    }

    private static void printHexadecimal(byte[] bytes) {
            for (byte b : bytes) {
                System.out.printf("%02X ", b);
            }
            System.out.println();
        }
}

And Console output is here :

ekla@Eklas-MacBook-Air RoughFolder % javac Main.java
ekla@Eklas-MacBook-Air RoughFolder % java Main
UTF-8 Hexadecimal:
48 65 6C 6C 6F 2C 20 E4 B8 96 E7 95 8C 
Latin-1 Hexadecimal:
48 65 6C 6C 6F 2C 20 C5 6C 61 6E 64 
UTF-8 Hexadecimal:
48 65 6C 6C 6F 2C 20 C3 85 6C 61 6E 64 
Hello, 世界
Hello, �land
Hello, Åland
cyrille-artho commented 2 weeks ago

Please set up the Scanner objects for all six possible combinations and encode the outcome as assertions. As we're now using Java 17, you can use the new utility function parseHex to encode the expected string content (use getBytes again on the resulting string, after the Scanner use, to see what the "weird" strings contain): https://stackoverflow.com/questions/11208479/how-do-i-initialize-a-byte-array-in-java

eklaDFF commented 2 weeks ago
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HexFormat;
import java.util.Scanner;

public class Main {
    public static void main(String[] args){
        String utf8String = "Hello, 世界";
        String latin1String = "Hello, Åland";

        byte[] utf8Bytes = utf8String.getBytes(StandardCharsets.UTF_8);
        byte[] latin1Bytes = latin1String.getBytes(StandardCharsets.ISO_8859_1);
        byte[] utf8Bytes2 = latin1String.getBytes(StandardCharsets.UTF_8);

        // Printing Hexadecimal values
        System.out.println("UTF-8 Hexadecimal:");
        printHexadecimal(utf8Bytes);

        System.out.println("Latin-1 Hexadecimal:");
        printHexadecimal(latin1Bytes);

        System.out.println("UTF-8 Hexadecimal (Latin-1 String):");
        printHexadecimal(utf8Bytes2);

        // Six possible combinations of decodings using Scanner
        String decodedUtf8FromUtf8 = decodeWithScanner(utf8Bytes, StandardCharsets.UTF_8);
        String decodedLatin1FromUtf8 = decodeWithScanner(utf8Bytes, StandardCharsets.ISO_8859_1);
        String decodedUtf8FromLatin1 = decodeWithScanner(latin1Bytes, StandardCharsets.UTF_8);
        String decodedLatin1FromLatin1 = decodeWithScanner(latin1Bytes, StandardCharsets.ISO_8859_1);
        String decodedUtf8FromUtf8_2 = decodeWithScanner(utf8Bytes2, StandardCharsets.UTF_8);
        String decodedLatin1FromUtf8_2 = decodeWithScanner(utf8Bytes2, StandardCharsets.ISO_8859_1);

        // Assertions
        assert Arrays.equals(utf8Bytes, decodedUtf8FromUtf8.getBytes(StandardCharsets.UTF_8));
        assert Arrays.equals(latin1Bytes, decodedLatin1FromLatin1.getBytes(StandardCharsets.ISO_8859_1));

        // Printing decoded strings
        System.out.println("Decoded from UTF-8 bytes (using UTF-8): " + decodedUtf8FromUtf8);
        System.out.println("Decoded from UTF-8 bytes (using Latin-1): " + decodedLatin1FromUtf8);
        System.out.println("Decoded from Latin-1 bytes (using UTF-8): " + decodedUtf8FromLatin1);
        System.out.println("Decoded from Latin-1 bytes (using Latin-1): " + decodedLatin1FromLatin1);
        System.out.println("Decoded from UTF-8 bytes of Latin-1 String (using UTF-8): " + decodedUtf8FromUtf8_2);
        System.out.println("Decoded from UTF-8 bytes of Latin-1 String (using Latin-1): " + decodedLatin1FromUtf8_2);

        // Printing hex values of decoded strings
        System.out.println("Hex of Decoded from UTF-8 bytes (using UTF-8):");
        printHexadecimal(decodedUtf8FromUtf8.getBytes(StandardCharsets.UTF_8));

        System.out.println("Hex of Decoded from UTF-8 bytes (using Latin-1):");
        printHexadecimal(decodedLatin1FromUtf8.getBytes(StandardCharsets.UTF_8));

        System.out.println("Hex of Decoded from Latin-1 bytes (using UTF-8):");
        printHexadecimal(decodedUtf8FromLatin1.getBytes(StandardCharsets.UTF_8));

        System.out.println("Hex of Decoded from Latin-1 bytes (using Latin-1):");
        printHexadecimal(decodedLatin1FromLatin1.getBytes(StandardCharsets.ISO_8859_1));

        System.out.println("Hex of Decoded from UTF-8 bytes of Latin-1 String (using UTF-8):");
        printHexadecimal(decodedUtf8FromUtf8_2.getBytes(StandardCharsets.UTF_8));

        System.out.println("Hex of Decoded from UTF-8 bytes of Latin-1 String (using Latin-1):");
        printHexadecimal(decodedLatin1FromUtf8_2.getBytes(StandardCharsets.UTF_8));

        // Checking "weird" string content by getting bytes
        byte[] weirdBytesUtf8FromUtf8 = decodedUtf8FromUtf8.getBytes(StandardCharsets.UTF_8);
        byte[] weirdBytesLatin1FromUtf8 = decodedLatin1FromUtf8.getBytes(StandardCharsets.UTF_8);
        byte[] weirdBytesUtf8FromLatin1 = decodedUtf8FromLatin1.getBytes(StandardCharsets.UTF_8);
        byte[] weirdBytesLatin1FromLatin1 = decodedLatin1FromLatin1.getBytes(StandardCharsets.ISO_8859_1);
        byte[] weirdBytesUtf8FromUtf8_2 = decodedUtf8FromUtf8_2.getBytes(StandardCharsets.UTF_8);
        byte[] weirdBytesLatin1FromUtf8_2 = decodedLatin1FromUtf8_2.getBytes(StandardCharsets.UTF_8);

        // Assertions for "weird" bytes
        assert Arrays.equals(weirdBytesUtf8FromUtf8, utf8Bytes);
        assert Arrays.equals(weirdBytesLatin1FromLatin1, latin1Bytes);
    }

    private static String decodeWithScanner(byte[] bytes, Charset charset) {
        Scanner scanner = new Scanner(new String(bytes, charset));
        StringBuilder result = new StringBuilder();
        while (scanner.hasNextLine()) {
            result.append(scanner.nextLine());
        }
        return result.toString();
    }

    private static void printHexadecimal(byte[] bytes) {
        HexFormat hexFormat = HexFormat.of();
        String hex = hexFormat.formatHex(bytes);
        System.out.println(hex);
    }
}

Output :

ekla@Eklas-MacBook-Air RoughFolder % javac Main.java
ekla@Eklas-MacBook-Air RoughFolder % java -ea Main
UTF-8 Hexadecimal:
48656c6c6f2c20e4b896e7958c
Latin-1 Hexadecimal:
48656c6c6f2c20c56c616e64
UTF-8 Hexadecimal (Latin-1 String):
48656c6c6f2c20c3856c616e64
Decoded from UTF-8 bytes (using UTF-8): Hello, 世界
Decoded from UTF-8 bytes (using Latin-1): Hello, 世界
Decoded from Latin-1 bytes (using UTF-8): Hello, �land
Decoded from Latin-1 bytes (using Latin-1): Hello, Åland
Decoded from UTF-8 bytes of Latin-1 String (using UTF-8): Hello, Åland
Decoded from UTF-8 bytes of Latin-1 String (using Latin-1): Hello, Ãland
Hex of Decoded from UTF-8 bytes (using UTF-8):
48656c6c6f2c20e4b896e7958c
Hex of Decoded from UTF-8 bytes (using Latin-1):
48656c6c6f2c20c3a4c2b8c296c3a7c295c28c
Hex of Decoded from Latin-1 bytes (using UTF-8):
48656c6c6f2c20efbfbd6c616e64
Hex of Decoded from Latin-1 bytes (using Latin-1):
48656c6c6f2c20c56c616e64
Hex of Decoded from UTF-8 bytes of Latin-1 String (using UTF-8):
48656c6c6f2c20c3856c616e64
Hex of Decoded from UTF-8 bytes of Latin-1 String (using Latin-1):
48656c6c6f2c20c3836c616e64
ekla@Eklas-MacBook-Air RoughFolder % 

Q1 : Should we use assert between encoded and decoded bytes for every possible combinations (6x6) ?

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Just to experiment, I tried on testCharBufferConstructor

Screenshot 2024-06-16 at 8 07 54 AM

And the Standard error is here

---------------------- JPF error stack trace ---------------------
gov.nasa.jpf.JPFNativePeerException: exception in native method java.lang.String.getBytes
    at gov.nasa.jpf.vm.NativeMethodInfo.executeNative(NativeMethodInfo.java:186)
    at gov.nasa.jpf.jvm.bytecode.EXECUTENATIVE.execute(EXECUTENATIVE.java:73)
    at gov.nasa.jpf.vm.ThreadInfo.executeInstruction(ThreadInfo.java:1910)
    at gov.nasa.jpf.vm.ThreadInfo.executeTransition(ThreadInfo.java:1861)
    at gov.nasa.jpf.vm.SystemState.executeNextTransition(SystemState.java:765)
    at gov.nasa.jpf.vm.VM.forward(VM.java:1721)
    at gov.nasa.jpf.search.Search.forward(Search.java:937)
    at gov.nasa.jpf.search.DFSearch.search(DFSearch.java:79)
    at gov.nasa.jpf.JPF.run(JPF.java:613)
    at gov.nasa.jpf.util.test.TestJPF.createAndRunJPF(TestJPF.java:675)
    at gov.nasa.jpf.util.test.TestJPF.noPropertyViolation(TestJPF.java:806)
    at gov.nasa.jpf.util.test.TestJPF.verifyNoPropertyViolation(TestJPF.java:830)
    at gov.nasa.jpf.test.java.nio.BufferTest.testCharBufferConstructor(BufferTest.java:57)
    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:568)
    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:568)
    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)
Caused by: java.lang.StringIndexOutOfBoundsException: begin 0, end 3, length 1
    at java.base/java.lang.String.checkBoundsBeginEnd(String.java:4606)
    at java.base/java.lang.String.getBytes(String.java:1732)
    at gov.nasa.jpf.vm.JPF_java_lang_String.getBytes__II_3BI__V(JPF_java_lang_String.java:111)
    at jdk.internal.reflect.GeneratedMethodAccessor13.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at gov.nasa.jpf.vm.NativeMethodInfo.executeNative(NativeMethodInfo.java:125)
    ... 55 more

Q2 : Would it be helpful if I share whole test report (test report from ./gradlew clean test command with you ?

Q3 : (not a question) I ran BufferTest.testCharBufferConstructor alone in IntelliJ. Below is the output of errors including Assertion Errors. It will help us to analyze the error related to only testCharBufferConstructor , because from ./gradlew clean test error output is mixed with testCharBufferConstructor and testByteBufferConstructor both.

testCharBufferConstructorReport.pdf

cyrille-artho commented 2 weeks ago

Thanks, this looks good. For creating the unit tests from this, please split the code into two chunks: One for the Japanese and one for the Swedish string. In the unit test, we can simply use assertNotEquals or a comparison to the hex arrays for the "weird" bytes. If we have six assertions, we can remove all the print statements (and their helper method). We can now also see that the logical issue with the indices/ranges across the byte range comes up again. Please share the output of the failing test; normally, you can see the interesting parts in the test output files, e.g., build/test-results/test/TEST-gov.nasa.jpf.test.java.nio.BufferTest.xml.

eklaDFF commented 2 weeks ago

"For creating the unit tests from this, please split the code into two chunks: One for the Japanese and one for the Swedish string." -----> Do we have to create two methods to replace testCharBufferConstructor() ?

"Please share the output of the failing test; normally, you can see the interesting parts in the test output files, e.g., build/test-results/test/TEST-gov.nasa.jpf.test.java.nio.BufferTest.xml." -----> tmpi_ph33w6.html.pdf [XML document converted to PDF]

cyrille-artho commented 2 weeks ago

Yes, please write two new unit tests to replace testCharBufferConstructor. After that, it is probably a good idea to split the tests into the parts that pass now and the parts that currently fail. The smaller a test is, the easier it is to debug. We can look further into their behavior once we have a small failing test.

eklaDFF commented 2 weeks ago

Please have a look [ assertTrue() is used in testByteBufferConstructor() ]

Screenshot 2024-06-18 at 10 10 50 PM
cyrille-artho commented 2 weeks ago

Hi, It's easier to read text if you copy just the listing instead of a screenshot. You can use ```java ... ``` to format the code.

If you decode a byte array to a string with the same encoding that was used to create the byte array, we expect the same string again. Therefore, I would name the last two local variables accordingly and simplify their names:

byte[] utf8FromUtf8 = ...
byte[] latin1FromUtf8 = ...

assertTrue(utf8FromUtf8.equals(uft8Bytes));
assertFalse(latin1FromUtf8.eqals(utf8Bytes));

Does that work?

eklaDFF commented 2 weeks ago
@Test
  public void testCharBufferConstructorJapanese(){
    String utf8String = "Hello, 世界";
    byte[] utf8Bytes = utf8String.getBytes(StandardCharsets.UTF_8);

    String decodedUtf8FromUtf8 = decodeWithScanner(utf8Bytes, StandardCharsets.UTF_8);
    String decodedLatin1FromUtf8 = decodeWithScanner(utf8Bytes, StandardCharsets.ISO_8859_1);

    byte[] utf8FromUtf8 = decodedUtf8FromUtf8.getBytes(StandardCharsets.UTF_8);
    byte[] latin1FromUtf8 = decodedLatin1FromUtf8.getBytes(StandardCharsets.UTF_8);

    assertTrue(utf8FromUtf8.equals(latin1FromUtf8));
    assertFalse(latin1FromUtf8.equals(utf8FromUtf8));
  }

  private static String decodeWithScanner(byte[] bytes, Charset charset) {
    Scanner scanner = new Scanner(new String(bytes, charset));
    StringBuilder result = new StringBuilder();
    while (scanner.hasNextLine()) {
      result.append(scanner.nextLine());
    }
    return result.toString();
  }

Here is the failure report : testCharBufferConstructorJapanese

java.lang.AssertionError: 
    at gov.nasa.jpf.util.test.TestJPF.fail(TestJPF.java:164)
    at gov.nasa.jpf.util.test.TestJPF.assertTrue(TestJPF.java:1174)
    at gov.nasa.jpf.util.test.TestJPF.assertTrue(TestJPF.java:1179)
    at gov.nasa.jpf.test.java.nio.BufferTest.testCharBufferConstructorJapanese(BufferTest.java:87)
    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:568)
    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:568)
    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)
eklaDFF commented 2 weeks ago

If we are running only asserFalse(......), our test passes :

@Test
  public void testCharBufferConstructorJapanese(){
    String utf8String = "Hello, 世界";
    byte[] utf8Bytes = utf8String.getBytes(StandardCharsets.UTF_8);

    String decodedUtf8FromUtf8 = decodeWithScanner(utf8Bytes, StandardCharsets.UTF_8);
    String decodedLatin1FromUtf8 = decodeWithScanner(utf8Bytes, StandardCharsets.ISO_8859_1);

    byte[] utf8FromUtf8 = decodedUtf8FromUtf8.getBytes(StandardCharsets.UTF_8);
    byte[] latin1FromUtf8 = decodedLatin1FromUtf8.getBytes(StandardCharsets.UTF_8);

    assertFalse(latin1FromUtf8.equals(utf8FromUtf8));
  }

if we look at the decoded string from our dummy program, here is the output :

Decoded from UTF-8 bytes (using UTF-8): Hello, 世界
Decoded from UTF-8 bytes (using Latin-1): Hello, ��

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Is the pace of working on our project is enough or I should increase to meet our goal ?

cyrille-artho commented 2 weeks ago

Try to extend the assertion so that the two strings are printed if they don't match (for assertTrue) or match by accident (for assertFalse). If you are stuck with one task, try to work on another task in parallel, so you don't have to wait for our feedback until the next day.

eklaDFF commented 2 weeks ago

(1/2) testCharBufferConstructorJapanese()

@Test
  public void testCharBufferConstructorJapanese(){
    String utf8String = "Hello, 世界";
    byte[] utf8Bytes = utf8String.getBytes(StandardCharsets.UTF_8);

    String decodedUtf8FromUtf8 = decodeWithScanner(utf8Bytes, StandardCharsets.UTF_8);
    String decodedLatin1FromUtf8 = decodeWithScanner(utf8Bytes, StandardCharsets.ISO_8859_1);

    byte[] utf8FromUtf8 = decodedUtf8FromUtf8.getBytes(StandardCharsets.UTF_8);
    byte[] latin1FromUtf8 = decodedLatin1FromUtf8.getBytes(StandardCharsets.ISO_8859_1);

    assertTrue(("Assertion failed. Original: " + utf8String + ", Decoded: " + decodedLatin1FromUtf8),utf8FromUtf8.equals(utf8Bytes));
    assertFalse(("Assertion failed. Original: " + utf8String + ", Decoded: " + decodedLatin1FromUtf8),latin1FromUtf8.equals(utf8Bytes));
  }

Here is the output :

> Task :compileAnnotationsJava UP-TO-DATE
> Task :processAnnotationsResources NO-SOURCE
> Task :annotationsClasses UP-TO-DATE
> Task :copyLibs UP-TO-DATE
> Task :compileJava UP-TO-DATE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :generateBuildInfo
> Task :generateVersion
> Task :copyResources
> Task :compileExamplesJava UP-TO-DATE
> Task :compileClassesJava UP-TO-DATE
> Task :processClassesResources NO-SOURCE
> Task :classesClasses UP-TO-DATE
> Task :compilePeersJava UP-TO-DATE
> Task :processPeersResources NO-SOURCE
> Task :peersClasses UP-TO-DATE
> Task :compileTestJava
> Task :compileModules UP-TO-DATE
> Task :compile UP-TO-DATE
> Task :createAnnotationsJar UP-TO-DATE
> Task :createClassloaderSpecificTestsJar UP-TO-DATE
> Task :createJpfClassesJar UP-TO-DATE
> Task :createJpfJar
> Task :createRunJpfJar UP-TO-DATE
> Task :createRunTestJar UP-TO-DATE
> Task :buildJars
> Task :processTestResources NO-SOURCE
> Task :testClasses

java.lang.AssertionError: Assertion failed. Original: Hello, 世界, Decoded: Hello, 世界
    at gov.nasa.jpf.util.test.TestJPF.fail(TestJPF.java:164)
    at gov.nasa.jpf.util.test.TestJPF.assertTrue(TestJPF.java:1174)
    at gov.nasa.jpf.test.java.nio.BufferTest.testCharBufferConstructorJapanese(BufferTest.java:90)
    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:568)
    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:568)
    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
gov.nasa.jpf.test.java.nio.BufferTest > testCharBufferConstructorJapanese FAILED
    java.lang.AssertionError at BufferTest.java:90
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 1s
18 actionable tasks: 6 executed, 12 up-to-date

if the assertion condition is false then it throws AssertionError with provided message (in our case 1st parameter of assertTrue() is the message). Do we have to handle them (I mean only printing the Strings without throwing any error) ?

One more thing in our testCharBufferConstructorJapanese() test, should we check verifyNoPropertyViolation() (I forgot to add this) ? Applying verifyNoPropertyViolation() cause :

java.lang.StringIndexOutOfBoundsException: begin 0, end 3, length 1
    at java.base/java.lang.String.checkBoundsBeginEnd(String.java:4606)
    at java.base/java.lang.String.getBytes(String.java:1732)
    at gov.nasa.jpf.vm.JPF_java_lang_String.getBytes__II_3BI__V(JPF_java_lang_String.java:111)
    at jdk.internal.reflect.GeneratedMethodAccessor11.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at gov.nasa.jpf.vm.NativeMethodInfo.executeNative(NativeMethodInfo.java:125)
    ... 55 more
eklaDFF commented 2 weeks ago

(2/2) testCharBufferConstructorSwedish()

@Test
  public void testCharBufferConstructorSwedish() {
    if (verifyNoPropertyViolation()) {
      String latin1String = "Hello, Åland";

      byte[] latin1BytesFromLatin1String = latin1String.getBytes(StandardCharsets.ISO_8859_1);
      byte[] utf8BytesFromLatin1String = latin1String.getBytes(StandardCharsets.UTF_8);

      String decodedUtf8FromLatin1 = decodeWithScanner(latin1BytesFromLatin1String, StandardCharsets.UTF_8);
      String decodedLatin1FromLatin1 = decodeWithScanner(latin1BytesFromLatin1String, StandardCharsets.ISO_8859_1);
      String decodedUtf8FromUtf8 = decodeWithScanner(utf8BytesFromLatin1String, StandardCharsets.UTF_8);
      String decodedLatin1FromUtf8 = decodeWithScanner(utf8BytesFromLatin1String, StandardCharsets.ISO_8859_1);

      byte[] utf8FromLatin1 = decodedUtf8FromLatin1.getBytes(StandardCharsets.UTF_8);
      byte[] latin1FromLatin1 = decodedLatin1FromLatin1.getBytes(StandardCharsets.ISO_8859_1);
      byte[] utf8FromUtf8 = decodedUtf8FromUtf8.getBytes(StandardCharsets.UTF_8);
      byte[] latin1FromUtf8 = decodedLatin1FromUtf8.getBytes(StandardCharsets.UTF_8);

      assertFalse(("Assertion failed. Original: " + latin1String + ", Decoded: " + decodedUtf8FromLatin1),utf8FromLatin1.equals(latin1BytesFromLatin1String));
      assertFalse(("Assertion failed. Original: " + latin1String + ", Decoded: " + decodedLatin1FromLatin1),latin1FromLatin1.equals(latin1BytesFromLatin1String));
      assertFalse(("Assertion failed. Original: " + latin1String + ", Decoded: " + decodedUtf8FromUtf8),utf8FromUtf8.equals(utf8BytesFromLatin1String));
      assertFalse(("Assertion failed. Original: " + latin1String + ", Decoded: " + decodedLatin1FromUtf8),latin1FromUtf8.equals(utf8BytesFromLatin1String));

    }
  }

This test passes successfully if we remove out verifyNoPropertyViolation(). Otherwise it also fails at the same StringIndexOutOfBoundsException

cyrille-artho commented 2 weeks ago

Hi, In the first test, make sure you use the right variables in the assertion:

    assertTrue(("Assertion failed. Original: " + utf8String + ", Decoded: " + decodedLatin1FromUtf8),utf8FromUtf8.equals(utf8Bytes));

You print the wrong string in the output, it should be decodedUtf8FromUtf8 instead of decodedLatin1FromUtf8.

cyrille-artho commented 2 weeks ago

In the second example, there is at least one similar error from copying some code and then (incompletely) modifying it. Feel free to split the second test into two tests if that makes it easier for you to validate the code. Having fewer local variables and cross-decodings might indeed help here.

eklaDFF commented 2 weeks ago
@Test
  public void testCharBufferConstructorJapanese(){
//    if(!verifyNoPropertyViolation()){
//      return;
//    }
    String utf8String = "Hello, 世界";
    byte[] utf8Bytes = utf8String.getBytes(StandardCharsets.UTF_8);

    String decodedUtf8FromUtf8 = decodeWithScanner(utf8Bytes, StandardCharsets.UTF_8);
    String decodedLatin1FromUtf8 = decodeWithScanner(utf8Bytes, StandardCharsets.ISO_8859_1);

    byte[] utf8FromUtf8 = decodedUtf8FromUtf8.getBytes(StandardCharsets.UTF_8);
    byte[] latin1FromUtf8 = decodedLatin1FromUtf8.getBytes(StandardCharsets.ISO_8859_1);

    assertTrue(("Assertion failed. Original: " + utf8String + ", Decoded: " + decodedUtf8FromUtf8),utf8FromUtf8.equals(utf8Bytes));
    assertFalse(("Assertion failed. Original: " + utf8String + ", Decoded: " + decodedLatin1FromUtf8),latin1FromUtf8.equals(utf8Bytes));
  }

  @Test
  public void testCharBufferConstructorSwedishEncodingWithLATIN1() {
//    if (verifyNoPropertyViolation()) {
      String latin1String = "Hello, Åland";

      byte[] latin1BytesFromLatin1String = latin1String.getBytes(StandardCharsets.ISO_8859_1);

      String decodedUtf8FromLatin1 = decodeWithScanner(latin1BytesFromLatin1String, StandardCharsets.UTF_8);
      String decodedLatin1FromLatin1 = decodeWithScanner(latin1BytesFromLatin1String, StandardCharsets.ISO_8859_1);

      byte[] utf8FromLatin1 = decodedUtf8FromLatin1.getBytes(StandardCharsets.UTF_8);
      byte[] latin1FromLatin1 = decodedLatin1FromLatin1.getBytes(StandardCharsets.ISO_8859_1);

      assertTrue(("Assertion failed. Original: " + latin1String + ", Decoded: " + decodedUtf8FromLatin1),utf8FromLatin1.equals(latin1BytesFromLatin1String));
      assertFalse(("Assertion failed. Original: " + latin1String + ", Decoded: " + decodedLatin1FromLatin1),latin1FromLatin1.equals(latin1BytesFromLatin1String));

//    }
  }

  @Test
  public void testCharBufferConstructorSwedishEncodingWithUTF(){
//    if(verifyNoPropertyViolation()){
      String latin1String = "Hello, Åland";

      byte[] utf8BytesFromLatin1String = latin1String.getBytes(StandardCharsets.UTF_8);

      String decodedUtf8FromUtf8 = decodeWithScanner(utf8BytesFromLatin1String, StandardCharsets.UTF_8);
      String decodedLatin1FromUtf8 = decodeWithScanner(utf8BytesFromLatin1String, StandardCharsets.ISO_8859_1);

      byte[] utf8FromUtf8 = decodedUtf8FromUtf8.getBytes(StandardCharsets.UTF_8);
      byte[] latin1FromUtf8 = decodedLatin1FromUtf8.getBytes(StandardCharsets.UTF_8);

      assertTrue(("Assertion failed. Original: " + latin1String + ", Decoded: " + decodedUtf8FromUtf8),utf8FromUtf8.equals(utf8BytesFromLatin1String));
      assertFalse(("Assertion failed. Original: " + latin1String + ", Decoded: " + decodedLatin1FromUtf8),latin1FromUtf8.equals(utf8BytesFromLatin1String));
//    }
  }

Failure Report for testCharBufferConstructorJapanese()

java.lang.AssertionError: Assertion failed. Original: Hello, 世界, Decoded: Hello, 世界
    at gov.nasa.jpf.util.test.TestJPF.fail(TestJPF.java:164)
    at gov.nasa.jpf.util.test.TestJPF.assertTrue(TestJPF.java:1174)
    at gov.nasa.jpf.test.java.nio.BufferTest.testCharBufferConstructorJapanese(BufferTest.java:90)
    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)
    ......................................................

Failure Report for testCharBufferConstructorSwedishEncodingWithLATIN1()

java.lang.AssertionError: Assertion failed. Original: Hello, Åland, Decoded: Hello, �land
    at gov.nasa.jpf.util.test.TestJPF.fail(TestJPF.java:164)
    at gov.nasa.jpf.util.test.TestJPF.assertTrue(TestJPF.java:1174)
    at gov.nasa.jpf.test.java.nio.BufferTest.testCharBufferConstructorSwedishEncodingWithLATIN1(BufferTest.java:108)
    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)
    ......................

Failure Report for testCharBufferConstructorSwedishEncodingWithUTF()

java.lang.AssertionError: Assertion failed. Original: Hello, Åland, Decoded: Hello, Åland
    at gov.nasa.jpf.util.test.TestJPF.fail(TestJPF.java:164)
    at gov.nasa.jpf.util.test.TestJPF.assertTrue(TestJPF.java:1174)
    at gov.nasa.jpf.test.java.nio.BufferTest.testCharBufferConstructorSwedishEncodingWithUTF(BufferTest.java:127)
    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:568)
    .........................................

Here is some doubts:

Q1: Why we are using assertTrue() and assertFalse() for two different scenarios ? Should we not use both of the assert statement for a particular pair of original string and decoded string to see which one causes error ?

Q2: Why verifyNoPropertyViolation() check is causing an error related to IndexOutOfBoundException ?

cyrille-artho commented 2 weeks ago

Thanks for the update. Please share your work in progress as a branch in your forked repository, so we can review it more easily. My answers:

  1. If we decode a string using the same encoding that was used before, the result should match the original. If the encoding parameter does not match, the result differs. Perhaps assertEquals and assertNotEquals are easier to understand here?
  2. At some point in the chain of conversions of the string from a String to a byte array and back, the internal representation of the string violates an invariant in JPF. (This is because something goes wrong in the interplay between JPF's representation of the string data and the methods of the underlying JVM.) I hope that the tests help us find the source of this discrepancy.
eklaDFF commented 2 weeks ago

https://github.com/eklaDFF/jpf-core/tree/bufferTestBranch

(I have commented out verifyNoPropertyViolation() check. If we uncomment it, will cause IndexOutOfBoundError.)

cyrille-artho commented 2 weeks ago

We get four failing tests with the following two exceptions:

  1. java.lang.NoSuchMethodError: java.nio.CharBuffer.position(I)Ljava/nio/CharBuffer (three times)
  2. java.lang.UnsatisfiedLinkError: cannot find native jdk.internal.misc.ScopedMemoryAccess.registerNatives (once)

The first one seems to be easy to address, by using the corresponding setter method in Buffer, which is a superclass to CharBuffer and ByteBuffer. For some reason, it is currently not called; adding a short method calling return super.position(position); may fix this.

The second one is part of the following stack trace:

        at jdk.internal.misc.ScopedMemoryAccess.registerNatives(no peer)
        at jdk.internal.misc.ScopedMemoryAccess.<clinit>(ScopedMemoryAccess.java
:83)
        at java.nio.BufferMismatch.<clinit>(BufferMismatch.java:35)
        at java.nio.ByteBuffer.equals(ByteBuffer.java:311)
        at gov.nasa.jpf.test.java.nio.BufferTest.testByteBufferConstructor(Buffe
rTest.java:45)
        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)

It looks like the helper method BufferMismatch is now too tightly integrated with the underlying JVM. The easiest way to fix this is by writing your own loop that checks the buffer contents:

    return BufferMismatch.mismatch(this, thisPos,
        that, thatPos,
        thisRem) < 0;

This loop checks the buffer contents, but it also causes class BufferMismatch to be loaded. The static initializer in that class has dependencies that cause JPF with Java 17 to fail. Replacing this call with a small loop results in minor code duplication but will avoid this dependency.

cyrille-artho commented 2 weeks ago

For the buffer content comparison in ByteBuffer.equals, we probably only need the equivalent of the final loop from the JDK implementation in https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/nio/BufferMismatch.java:

        for (int i = 0; i < length; i++) {
            if (a.get(aOff + i) != b.get(bOff + i))
                return i;
        }
        return -1;

(Note that the parameter names will be different when you adapt this loop to JPF's ByteBuffer.equals.) We don't need the complex internally optimized code from the top of each implementation of equals in the JDK 17 source, as that is likely only going to work with the low-level optimized data structures in the host JVM. JPF has its own buffer array, so these optimizations wouldn't apply.

eklaDFF commented 2 weeks ago
  1. We have implemented the position(I) in CharBuffer.java and the one test just passed for testByteBufferConstructor() .
  2. We have implemented the equals() method as you guided :

    public boolean equals(Object ob) {
    if (this == ob)
      return true;
    if (!(ob instanceof ByteBuffer))
      return false;
    ByteBuffer that = (ByteBuffer)ob;
    int thisPos = this.position();
    int thisRem = this.limit() - thisPos;
    int thatPos = that.position();
    int thatRem = that.limit() - thatPos;
    if (thisRem < 0 || thisRem != thatRem)
      return false;
    return mismatch(this, thisPos,
        that, thatPos,
        thisRem) < 0;
    }
    
    // This method is written to replace BufferMismatch.mismatch(ByteBuffer a, int aOff, ByteBuffer b, int bOff, int length) in equals(Object ob) in this class
    private int mismatch(ByteBuffer a, int aOff, ByteBuffer b, int bOff, int length){
    for (int i = 0; i < length; i++) {
      if (a.get(aOff + i) != b.get(bOff + i))
        return i;
    }
    return -1;
    }

    But our test still fails with

    Caused by: java.lang.StringIndexOutOfBoundsException: begin 0, end 3, length 1
    at java.base/java.lang.String.checkBoundsBeginEnd(String.java:4606)
    at java.base/java.lang.String.getBytes(String.java:1732)
    at gov.nasa.jpf.vm.JPF_java_lang_String.getBytes__II_3BI__V(JPF_java_lang_String.java:111)
    at jdk.internal.reflect.GeneratedMethodAccessor11.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at gov.nasa.jpf.vm.NativeMethodInfo.executeNative(NativeMethodInfo.java:125)
    ... 55 more
cyrille-artho commented 2 weeks ago

Thanks. This outcome was kind of expected; your replacement of mismatch avoids a problem with JVM-specific classes but does not fix the issue that internal assumptions about the length and content of the byte buffers are wrong under Java 17. I assume this is because of Latin-1-encoded strings. What is the status of your new unit tests now (the ones that convert to bytes and back to a string)? Please make PRs for the fixes that you have implemented this far.

eklaDFF commented 1 week ago

PR : #463

What is the status of your new unit tests now (the ones that convert to bytes and back to a string)? testCharBufferConstructorJapanese(), testCharBufferConstructorSwedishEncodingWithLATIN1() and testCharBufferConstructorSwedishEncodingWithUTF() fails at verifyNoPropertyViolation() throwing out error as below.

gov.nasa.jpf.JPFNativePeerException: exception in native method java.lang.String.getBytes
    at gov.nasa.jpf.vm.NativeMethodInfo.executeNative(NativeMethodInfo.java:186)
    at gov.nasa.jpf.jvm.bytecode.EXECUTENATIVE.execute(EXECUTENATIVE.java:73)
    at gov.nasa.jpf.vm.ThreadInfo.executeInstruction(ThreadInfo.java:1910)
    at gov.nasa.jpf.vm.ThreadInfo.executeTransition(ThreadInfo.java:1861)
    at gov.nasa.jpf.vm.SystemState.executeNextTransition(SystemState.java:765)
    at gov.nasa.jpf.vm.VM.forward(VM.java:1721)
    at gov.nasa.jpf.search.Search.forward(Search.java:937)
    at gov.nasa.jpf.search.DFSearch.search(DFSearch.java:79)
    at gov.nasa.jpf.JPF.run(JPF.java:613)
    at gov.nasa.jpf.util.test.TestJPF.createAndRunJPF(TestJPF.java:675)
    at gov.nasa.jpf.util.test.TestJPF.noPropertyViolation(TestJPF.java:806)
    at gov.nasa.jpf.util.test.TestJPF.verifyNoPropertyViolation(TestJPF.java:830)
    at gov.nasa.jpf.test.java.nio.BufferTest.testCharBufferConstructorSwedishEncodingWithUTF(BufferTest.java:87)
    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:568)
    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:568)
    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)
Caused by: java.lang.StringIndexOutOfBoundsException: begin 0, end 3, length 1
    at java.base/java.lang.String.checkBoundsBeginEnd(String.java:4606)
    at java.base/java.lang.String.getBytes(String.java:1732)
    at gov.nasa.jpf.vm.JPF_java_lang_String.getBytes__II_3BI__V(JPF_java_lang_String.java:111)
    at jdk.internal.reflect.GeneratedMethodAccessor11.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at gov.nasa.jpf.vm.NativeMethodInfo.executeNative(NativeMethodInfo.java:125)
    ... 55 more

java.lang.AssertionError: JPF internal exception executing: :gov.nasa.jpf.JPFNativePeerException: exception in native method java.lang.String.getBytes
    at gov.nasa.jpf.util.test.TestJPF.fail(TestJPF.java:164)
    at gov.nasa.jpf.util.test.TestJPF.fail(TestJPF.java:156)
    at gov.nasa.jpf.util.test.TestJPF.noPropertyViolation(TestJPF.java:810)
    at gov.nasa.jpf.util.test.TestJPF.verifyNoPropertyViolation(TestJPF.java:830)
    at gov.nasa.jpf.test.java.nio.BufferTest.testCharBufferConstructorSwedishEncodingWithUTF(BufferTest.java:87)
    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:568)
    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:568)
    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)
cyrille-artho commented 1 week ago

Thanks, it is a but surprising that this fails also for the Swedish string, because that letter translates into one byte (Latin-1) or two bytes (UTF-8), yet the mismatch in the index is several bytes. Please look at one of the failing tests in more detail. What are the values of the key variables (begin, end, length) when using the string in different encodings?

eklaDFF commented 1 week ago

Taking below test as reference :

@Test
  public void testCharBufferConstructorJapanese(){
    if(verifyNoPropertyViolation()){
        String utf8String = "Hello, 世界";
        byte[] utf8Bytes = utf8String.getBytes(StandardCharsets.UTF_8);

        String decodedUtf8FromUtf8 = decodeWithScanner(utf8Bytes, StandardCharsets.UTF_8);
        String decodedLatin1FromUtf8 = decodeWithScanner(utf8Bytes, StandardCharsets.ISO_8859_1);

        byte[] utf8FromUtf8 = decodedUtf8FromUtf8.getBytes(StandardCharsets.UTF_8);
        byte[] latin1FromUtf8 = decodedLatin1FromUtf8.getBytes(StandardCharsets.ISO_8859_1);

        assertTrue(("Assertion failed. Original: " + utf8String + ", Decoded: " + decodedUtf8FromUtf8),utf8FromUtf8.equals(utf8Bytes));
        assertFalse(("Assertion failed. Original: " + utf8String + ", Decoded: " + decodedLatin1FromUtf8),latin1FromUtf8.equals(utf8Bytes));
    }
  }

Our error for java.lang.StringIndexOutOfBoundsException: begin 0, end 3, length 1 is detected at verifyNoPropertyViolation() for all those three tests. So, I think our String (Japanese or Swedish) is actually not involved because they are declared outside.

cyrille-artho commented 1 week ago

Method verifyNoPropertyViolation is quite unusual in that is passes control to JPF. So all the code inside the curly braces is executed within JPF. The stack trace shows that line 87 in the test causes the problem; which line is that in the code snippet above?

eklaDFF commented 1 week ago

87 : if (verifyNoPropertyViolation(){

Screenshot 2024-06-25 at 2 25 18 PM
cyrille-artho commented 1 week ago

I don't quite understand what you want to show with the screenshot above. Please elaborate.

eklaDFF commented 1 week ago

"The stack trace shows that line 87 in the test causes the problem; which line is that in the code snippet above?" -> On line number 87, verifyNoPropertyViolation() is mentioned.

The question is, when the java.lang.StringIndexOutOfBoundsException: begin 0, end 3, length 1 is thrown at line 87 then how our string String utf8String = "Hello, 世界"; is involved ?

Question 1: In any case in above context, why the testByteBuffer() test passes, verifyNoPropertyViolation() too ?

cyrille-artho commented 1 week ago

Of course, that is the line which calls JPF. So we cannot see which line caused the problem inside JPF from this stack trace. (Hence, it is desirable to keep all JPF unit tests small.)

The test testByteBufferConstructor passes because it uses only ASCII characters, which always encode to one single byte each, regardless of the encoding.

I think the best way to see why exactly the test above fails is to print intermediate information within JPF's native peer methods that handle byte buffers. Then, you can see the buffer content as well as begin, end, and length at different stages (during different method calls). Somewhere, some of these values are no longer consistent. Seeing where this happens also lets us understand why it happens.

eklaDFF commented 4 days ago

Please explain in steps I should go for this, as it is too complicated to find the right direction to move in.

"I think the best way to see why exactly the test above fails is to print intermediate information within JPF's native peer methods that handle byte buffers. Then, you can see the buffer content as well as begin, end, and length at different stages (during different method calls). Somewhere, some of these values are no longer consistent. Seeing where this happens also lets us understand why it happens." -----> I tried to print them in console, worth to share here with you ?

eklaDFF commented 3 days ago

In our model class java.lang.String, we ignored the byte coder params :

public void getBytes(byte[] dst, int srcPos, int dstBegin, byte coder, int length){
    getBytes(srcPos,(srcPos+length),dst,dstBegin);
}

But in case of OpenJDK, it have been implemented indirectly :

@Deprecated(since="1.1")
    public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) {
        checkBoundsBeginEnd(srcBegin, srcEnd, length());
        Objects.requireNonNull(dst);
        checkBoundsOffCount(dstBegin, srcEnd - srcBegin, dst.length);
        if (isLatin1()) {
            StringLatin1.getBytes(value, srcBegin, srcEnd, dst, dstBegin);
        } else {
            StringUTF16.getBytes(value, srcBegin, srcEnd, dst, dstBegin);
        }
    }

Overall, my question is, ignoring the param byte coder our jpf might be executing it assuming ASCII ??


How coder is being used ?

In above OpenJDK code for getBytes(), there is checkBoundsBeginEnd(srcBegin, srcEnd, length()); and it is the point of error in this function/method.

Code for length() :

public int length() {
    return value.length >> coder();
 }
byte coder() {
    return COMPACT_STRINGS ? coder : UTF16;
}
cyrille-artho commented 3 days ago

Yes, this makes sense! Value coder is 0 for UTF8 and 1 for LATIN1. This means that for LATIN1, the internal length for the byte array is double that of the string. (For UTF8, it's more complicated.) So we have to use that value in JPF's code, too, to get the correct results.

eklaDFF commented 3 days ago

How can we pass this coder ?

Approach 1 : There is a method suitable to this in OpenJDK's String.java but that is not public, so we cannot access them.

Approach 2 : I thought we can initialize the variable coder of our model class String.java in our method getBytes(byte[] dst, int srcPos, int dstBegin, byte coder, int length). But for this we have make coder non-final variable. Even we do, problem remains same.

How can we pass this coder ??

cyrille-artho commented 3 days ago

Our current strategy is that JPF's String model class has its own methods to convert data, but we fall back to some native methods (of the host JVM) for operations like upper/lower case conversions, trim, etc.

JPF's model class String already uses coder, but there may be a place or two where it is not used yet, or used incorrectly. Please review where in JPF's model class the internal information starts to diverge from the ground truth, so we can address this.

eklaDFF commented 2 days ago

Don't know correct or not.

from our native method getBytes() in model class we are going to OpenJDK String class. It might we that they are loading their own coder value. So, tried below implementation :

public void getBytes(byte[] dst, int srcPos, int dstBegin, byte coder, int length){
//      getBytes(srcPos,(srcPos+length),dst,dstBegin);
        if (isLatin1()) {
            StringLatin1.getBytes(value,srcPos,(srcPos+length),dst,dstBegin);
        } else {
            StringUTF16.getBytes(value,srcPos,(srcPos+length),dst,dstBegin);
        }

    }

Is this correct direction to look in ? "JPF's model class String already uses coder, but there may be a place or two where it is not used yet, or used incorrectly. Please review where in JPF's model class the internal information starts to diverge from the ground truth, so we can address this." -----> As fas as I looked, there is nothing like. Actually our model String class is messed up, and coder is final so looked them into constructors only.

cyrille-artho commented 2 days ago

What happened here is that the model class String was developed over 20+ years, and changes were made incrementally, just enough to keep it working with given versions of Java under common usage of strings. Model classes often simplify operations, by design and as a way to keep them simple. Such changes may over time accumulate in "local" solutions to problems affecting only one method or a few methods. At class level or across all methods, such solutions may be ultimately suboptimal or even contradictory and require a larger refactoring (or fix) to really work again across all use cases. This is the situation we have now for String, where coder is partly used, but not consistently or throughout the whole class.

eklaDFF commented 2 days ago

You have larger overview of this problem. What next should I do ?

Is this (below) not correct approach for jpf ? We will remove this if jpf purpose is compromised because here we are ultimately handing over to the host JVM. One important thing here is that we are checking the coder value here itself, not in the host JVM

public void getBytes(byte[] dst, int srcPos, int dstBegin, byte coder, int length){
  //    getBytes(srcPos,(srcPos+length),dst,dstBegin);
    if (isLatin1()) {
        StringLatin1.getBytes(value,srcPos,(srcPos+length),dst,dstBegin);
    } else {
        StringUTF16.getBytes(value,srcPos,(srcPos+length),dst,dstBegin);
    }

}
cyrille-artho commented 2 days ago

Yes, I think this is the right approach. We have to store the content of the string in the model class, as string instances at the native level (on the host JVM) would not be backtracked. I think that using the right decoding method based on isLatin1() (which internally checks the value of coder) is the right approach here.

eklaDFF commented 2 days ago

Here is error after above code implementation :

running jpf with args:
JavaPathfinder core system v8.0 (rev d55aee7ec38835996927eaae32ab27538a450f42) - (C) 2005-2014 United States Government. All rights reserved.

====================================================== system under test
gov.nasa.jpf.test.java.nio.BufferTest.runTestMethod()

====================================================== search started: 04/07/24, 12:36 am
[WARNING] orphan NativePeer method: jdk.internal.misc.Unsafe.getUnsafe()Lsun/misc/Unsafe;
[WARNING] orphan NativePeer method: java.lang.StringCoding.decode([BII)[C
[WARNING] orphan NativePeer method: java.lang.StringCoding.encode([CII)[B
[WARNING] orphan NativePeer method: java.util.ResourceBundle.getClassContext()[Ljava/lang/Class;

====================================================== error 1
gov.nasa.jpf.vm.NoUncaughtExceptionsProperty
java.lang.NoSuchMethodError: java.nio.CharBuffer.isReadOnly()Z
    at java.io.Reader.read(Reader.java:188)
    at java.util.Scanner.readInput(Scanner.java:882)
    at java.util.Scanner.findWithinHorizon(Scanner.java:1796)
    at java.util.Scanner.hasNextLine(Scanner.java:1610)
    at gov.nasa.jpf.test.java.nio.BufferTest.decodeWithScanner(BufferTest.java:106)
    at gov.nasa.jpf.test.java.nio.BufferTest.testCharBufferConstructorJapanese(BufferTest.java:55)
    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)
/*   Tells whether or not this buffer is read-only.
*     return true if, and only if, this buffer is read-only
*/
public abstract boolean isReadOnly();

In case of isReadOnly(), what functionality our jpf require ? It is difficult to find the implementation of this as it is abstract method in java.nio.Buffer.java.


One more thing I want know, in github repo for OpenJDK17, there is no java.nio.CharBuffer.java

Screenshot 2024-07-04 at 1 22 18 AM

But when I see in IntelliJ, it have

Screenshot 2024-07-04 at 1 25 10 AM
cyrille-artho commented 1 day ago

It's not clear from the OpenJDK 17 source code when/why a buffer is read-only or not. I think the best way to find out is to try a few examples and run them in the regular JVM:

  1. A CharBuffer backing a String.
  2. A newly created buffer (e.g., by calling ByteBuffer.allocate).
  3. A ByteBuffer relating to an InputStream.
  4. A ByteBuffer relating to an OutputStream.

In addition to examples, also try to find documentation. Most likely, isReadOnly will return true, false, true, false, for cases 1–4.