Details
-
Type:
Bug
-
Status:
Open
-
Priority:
Minor
-
Resolution: Unresolved
-
Affects Version/s: 1.8.0
-
Fix Version/s: None
-
Component/s: None
-
Labels:None
-
Environment:Windows 7 x64, Eclipse 3.7, JRE1.6, Groovy 1.8.0
-
Testcase included:yes
-
Number of attachments :
Description
Problem with the order of evaluation of ByteArrayInputStream operations after Groovy 1.7.10. the Below code executes properly in Groovy 1.6 and 1.7.10, but throw an ArrayIndexOutOfBoundsException in Groovy 1.8.
void testIndexingFailure(){
byte[] buffer = new byte[4]
buffer[0] = 0xB
buffer[1] = 0xE
buffer[2] = 0xE
buffer[3] = 0xF
ByteArrayInputStream bis = new ByteArrayInputStream(buffer)
byte[] bArray = new byte[bis.available()]
while(bis.available() != 0){
bArray[bis.pos] = bis.read()
}
}
The issue seems to be in the order of evaluation of the line:
bArray[bis.pos] = bis.read()
In the previous version of Groovy this evaluated the bis.pos before doing the read operation. The latest versions now evaluate the bis.read(), which increments bis.pos before evaluating the bis.pos. the end result being that the first byte read is stored in bArray[1] and an attempt is made to store the last byte one position past the end of the array.
A test made with an translation of this code block into Java:
public void testIndexingFailure2(){ byte[] buffer = new byte[4]; buffer[0] = 'b'; buffer[1] = 'e'; buffer[2] = 'e'; buffer[3] = 'f'; ByteArrayInputStream bis = new ByteArrayInputStream(buffer); byte[] bArray = new byte[bis.available()]; while(bis.available() != 0){ bArray[((ByteArrayInputStream) bis).pos] = (byte) bis.read(); } }
Reveals that bis.pos is a protected value of the ByteArrayInputStream and cannot be accesses in this way. A further test using a customized ByteArrayInputStream so that the pos value was public revealed that the operation:
bArray[((ByteArrayInputStream) bis).pos] = (byte) bis.read();
Evaluates as the earlier versions of Groovy did and does not throw the ArrayIndexOutOfBoundsException that the new Groovy 1.8 does.
The work around identified is to use the Integer java.io.ByteArrayInputStream.read(byte[] b, int off, int len) overload which avoids this problem all together, but if there is an underlying condition in the recent performance enhancements, it may provoke other undesirable behaviors in otherwise innocuous assignments.
class IndexingBug extends GroovyTestCase { void testByteArrrayInputStreamIndexingEvaluation(){ byte[] buffer = new byte[4] buffer[0] = 0xB buffer[1] = 0xE buffer[2] = 0xE buffer[3] = 0xF ByteArrayInputStream bis = new ByteArrayInputStream(buffer) byte[] bArray = new byte[bis.available()] while(bis.available() != 0){ bArray[bis.pos] = bis.read() } assertEquals("bArray should equal the original buffer", buffer, bArray) } void testByteArrrayInputStreamManualIndexingEvaluation(){ byte[] buffer = new byte[4] buffer[0] = 0xB buffer[1] = 0xE buffer[2] = 0xE buffer[3] = 0xF ByteArrayInputStream bis = new ByteArrayInputStream(buffer) byte[] bArray = new byte[bis.available()] int endIndex = bis.available() int position = bis.pos while(position != endIndex){ bArray[position] = bis.read() position++ } assertEquals("bArray should equal the original buffer", buffer, bArray) } void testAlternateReadPass(){ byte[] buffer = new byte[4] buffer[0] = 0xB buffer[1] = 0xE buffer[2] = 0xE buffer[3] = 0xF ByteArrayInputStream bis = new ByteArrayInputStream(buffer) byte[] bArray = new byte[bis.available()] while(bis.available() != 0){ bis.read(bArray, 0 , bis.available()) } assertEquals("bArray should equal the original buffer", buffer, bArray) } }