Groovy Badness: Turn a List into a Map

I’ve always enjoyed finding bugs in languages. I still fondly recall finding a bug in Perl concerning the “while () { .. }” construct and file names that evaluate to ‘false’, like a file named “0”, for example.

More recently, I discovered a Groovy bug in groovy.lang.SpreadMap class (and therefore in the List.toSpreadMap() implementation). I’ve reported it (GROOVY-6403) and submitted a patch with a unit test.

Some observations:

  • This implementation was horribly broken. Two seconds of review is enough for competent Java developers to see why.
  • This code has been around a long time. This Groovy Goodness post is dated January, 2010. It shows the two methods (.size() and .get()) that were implemented correctly. I’ll wonder forever if all the other broken methods (.containsKey(), .keySet(), .isEmpty(), etc.) were missed or purposefully omitted.
  • This code could have been easily detected as flawed with a 5-line unit test.

The original example, with some additional (failing) asserts:

#!/usr/bin/env groovy
def list = ['key', 'value', 'name', 'tim'] as Object[]
def map = list.toSpreadMap()

assert 2 == map.size()                // ok
assert 'value' == map.key             // ok
assert true == map.containsKey('key') // FAIL
assert false == map.isEmpty()         // FAIL
assert 2 == map.keySet().size()       // FAIL

Back to the Perl bug

For these code snippets, place files named “0”, “1” and “2” in the current directory. Perl 5 was used for this testing.

The one will print “File is 0”, “File is 1” and “File is 2”, but also prints “How did we get here if 0 is not true?” [In earlier versions of Perl, this would not print anything if the first file found was “0”. They’ve changed that. But, see the next example.]

#!/usr/bin/env perl
while ($file = ) {
   print "File is $file\n";
   if (! $file) {
      print "Huh?  How did we get here if $file is not true?\n";
   }
}

This one just won’t print anything, since “0” is the first file it sees, and that evaluates to ‘false’. The extra variable assignment causes the hack fix to fail:

#!/usr/bin/env perl
my $inside;
my $file;
while ($file = ($inside = )) {
   print "File is $file\n";
}

P.S. In case it is not obvious, do not use either of these constructs. In summary:

  • For the Perl bug, use opendir() and readdir().
  • For the Groovy bug, look at what the constructor of SpreadMap.java does, and write your code that way. Or, use Groovy 2.2.0-rc-3+ or 2.1.10+
This entry was posted in Software Engineering. Bookmark the permalink.