Bug in String.indexOf()?! No way?!?

I was looking through Groovy JIRA and I run into GROOVY-5858 issue. In short it is about Groovy’s String.count() metamethod which hangs forever when it gets an empty string as an argument:

'any string'.count('')

I dug deeper into it and how surprised I was when it came out that Groovy’s String.count() method fails due to bug in Java String.indexOf(String str, int fromIndex) method. Basically the contract (by javadoc) for indexOf() method is that it returns the smallest index at which the next occurrence of str is found but not lower than fromIndex. If such an index does not exist (probably due to no more occurrences) than it shall return -1. So that is the theory – when it comes to practice user might expect that if the returned index is positive (what means that next occurrence is found) then it will not be lower than fromIndex passed as an argument to the method execution.
Here is the example that shows the above contract is not obeyed by String.indexOf() method:

// given
String strToLookIn = "bug";
String strToLookFor = "";
int fromIndex = 4;

// when
int k = strToLookIn.indexOf(strToLookFor, fromIndex);

// then
assertTrue((k == -1) || (k > fromIndex), String.format("k expected to be in [-1, %d..+oo] but is %d", fromIndex, k));

When the test is run it fails with the following description:

java.lang.AssertionError: k expected to be in [-1, 4..+oo] but is 3 expected [true] but found [false]

This proves that the most basic contract for String.indexOf() method is broken. More generally bug appears always when strToLookFor is empty and fromIndex is greater than length of string in which we look in.
Since Groovy developers trusted String.indexOf() contract they (and we all ;)) have a bug in Groovy now.
Just for a reference here is how Grooovy’s String.count() method is implemented:

public static int count(String self, String text) {
  int answer = 0;
  for (int idx = 0; true; idx++) {
    idx = self.indexOf(text, idx);
    if (idx >= 0) {
      ++answer;
    } else {
      break;
    }
  }
  return answer;
}

Exit condition from the above for loop is idx variable to become negative. Unfortunately due to the mentioned bug in String class it never happens for text variable being an empty string.
I’ve submitted a bug report to Oracle and hope it will be processed by the next end of the world which is 2018 😉 Personally I think it might never get fixed as the risk concerned with it might be simply too high. Basically up to now few billions of devices are running the broken String implementation and everyone is happy 😉
I used Oracle JDK 7u10 for the purpose of my investigation but probably the bug is present in (almost) all other versions. As it is quite obvious OpenJDK is impacted as well. Other vendors’ JDKs might not be impacted as they use completely different implementations for String.indexOf() method.

One thought on “Bug in String.indexOf()?! No way?!?

  1. The only difference is inesatd of doing :if(strpos_idx($someStr, “http”) != -1){//http was found in the string} else {//no http found}You would do :if(strpos($someStr, “http”) !== FALSE) {//http was found in the string} else {//no http found}So no mather if it was returning -1 or FALSE you still have to do a test. It changes nothing to the way you would have done your script and it doesn’t cause any problem to script in any circumstance. And it’s a bug ?This is being called a bug only because of the opinion of the author and his opinion is highly debatable.

Leave a Reply

Your email address will not be published. Required fields are marked *