title: Why does Java allow control characters in its identifiers?
body: <h1>The Mystery</h1>
  
  <p>In exploring precisely which characters were permitted in Java identifiers, I have stumbled upon something so extremely curious that it seems nearly certain to be a bug. </p>
  
  <p>I’d expected to find that Java identifiers conformed to the requirement that they start with characters that have the Unicode property <code>ID_Start</code> and are followed by those with the property <code>ID_Continue</code>, with an exception granted for leading underscores and for dollar signs. That did not prove to be the case, and what I found is at extreme variance with that or any other idea of a normal identifier that I have heard of.</p>
  
  <h1>Short Demo</h1>
  
  <p>Consider the following demonstration proving that an ASCII ESC character (octal 033) is permitted in Java identifiers:</p>
  
  <pre><code>$ perl -le 'print qq(public class escape { public static void main(String argv[]) { String var_\033 = "i am escape: \033"; System.out.println(var_\033); }})' &gt; escape.java
  $ javac escape.java
  $ java escape | cat -v
  i am escape: ^[
  </code></pre>
  
  <p>It’s even worse than that, though.  Almost infinitely worse, in fact.  Even NULLs are permitted! And thousands of other code points that are not even identifier characters. I have tested this on Solaris, Linux, and a Mac running Darwin, and all give the same results.</p>
  
  <h1>Long Demo</h1>
  
  <p>Here is a test program that will show all these unexpected code points that Java quite outrageuosly allows as part of a legal identifier name.</p>
  
  <pre><code>#!/usr/bin/env perl
  # 
  # test-java-idchars - find which bogus code points Java allows in its identifiers
  # 
  #   usage: test-java-idchars [low high]
  #   e.g.:  test-java-idchars 0 255
  #
  # Without arguments, tests Unicode code points
  # from 0 .. 0x1000.  You may go further with a
  # higher explicit argument.
  #
  # Produces a report at the end.
  #
  # You can ^C it prematurely to end the program then
  # and get a report of its progress up to that point.
  #
  # Tom Christiansen
  # tchrist@perl.com
  # Sat Jan 29 10:41:09 MST 2011
  
  use strict;
  use warnings;
  
  use encoding "Latin1";
  use open IO =&gt; ":utf8";
  
  use charnames ();
  
  $| = 1;
  
  my @legal;
  
  my ($start, $stop) = (0, 0x1000);
  
  if (@ARGV != 0) {
      if (@ARGV == 1) {
          for (($stop) = @ARGV) { 
              $_ = oct if /^0/;   # support 0OCTAL, 0xHEX, 0bBINARY
          }
      }
      elsif (@ARGV == 2) {
          for (($start, $stop) = @ARGV) { 
              $_ = oct if /^0/;
          }
      } 
      else {
          die "usage: $0 [ [start] stop ]\n";
      } 
  } 
  
  for my $cp ( $start .. $stop ) {
      my $char = chr($cp);
  
      next if $char =~ /[\s\w]/;
  
      my $type = "?";
      for ($char) {
          $type = "Letter"      if /\pL/;
          $type = "Mark"        if /\pM/;
          $type = "Number"      if /\pN/;
          $type = "Punctuation" if /\pP/;
          $type = "Symbol"      if /\pS/;
          $type = "Separator"   if /\pZ/;
          $type = "Control"     if /\pC/;
      } 
      my $name = $cp ? (charnames::viacode($cp) || "&lt;missing&gt;") : "NULL";
      next if $name eq "&lt;missing&gt;" &amp;&amp; $cp &gt; 0xFF;
      my $msg = sprintf("U+%04X %s", $cp, $name);
      print "testing \\p{$type} $msg...";
      open(TESTPROGRAM, "&gt;:utf8", "testchar.java") || die $!;
  
  print TESTPROGRAM &lt;&lt;"End_of_Java_Program";
  
  public class testchar { 
      public static void main(String argv[]) { 
          String var_$char = "variable name ends in $msg";
          System.out.println(var_$char); 
      }
  }
  
  End_of_Java_Program
  
      close(TESTPROGRAM) || die $!;
  
      system q{
          ( javac -encoding UTF-8 testchar.java \
              &amp;&amp; \
            java -Dfile.encoding=UTF-8 testchar | grep variable \
          ) &gt;/dev/null 2&gt;&amp;1
      };
  
      push @legal, sprintf("U+%04X", $cp) if $? == 0;
  
      if ($? &amp;&amp; $? &lt; 128) {
          print "&lt;interrupted&gt;\n";
          exit;  # from a ^C
      } 
  
      printf "is %s in Java identifiers.\n",  
          ($? == 0) ? uc "legal" : "forbidden";
  
  } 
  
  END { 
      print "Legal but evil code points: @legal\n";
  }
  </code></pre>
  
  <p>Here is a sample of running that program on just the first 33 code points that are neither whitespace nor identifier characters:</p>
  
  <pre><code>$ perl test-java-idchars 0 0x20
  testing \p{Control} U+0000 NULL...is LEGAL in Java identifiers.
  testing \p{Control} U+0001 START OF HEADING...is LEGAL in Java identifiers.
  testing \p{Control} U+0002 START OF TEXT...is LEGAL in Java identifiers.
  testing \p{Control} U+0003 END OF TEXT...is LEGAL in Java identifiers.
  testing \p{Control} U+0004 END OF TRANSMISSION...is LEGAL in Java identifiers.
  testing \p{Control} U+0005 ENQUIRY...is LEGAL in Java identifiers.
  testing \p{Control} U+0006 ACKNOWLEDGE...is LEGAL in Java identifiers.
  testing \p{Control} U+0007 BELL...is LEGAL in Java identifiers.
  testing \p{Control} U+0008 BACKSPACE...is LEGAL in Java identifiers.
  testing \p{Control} U+000B LINE TABULATION...is forbidden in Java identifiers.
  testing \p{Control} U+000E SHIFT OUT...is LEGAL in Java identifiers.
  testing \p{Control} U+000F SHIFT IN...is LEGAL in Java identifiers.
  testing \p{Control} U+0010 DATA LINK ESCAPE...is LEGAL in Java identifiers.
  testing \p{Control} U+0011 DEVICE CONTROL ONE...is LEGAL in Java identifiers.
  testing \p{Control} U+0012 DEVICE CONTROL TWO...is LEGAL in Java identifiers.
  testing \p{Control} U+0013 DEVICE CONTROL THREE...is LEGAL in Java identifiers.
  testing \p{Control} U+0014 DEVICE CONTROL FOUR...is LEGAL in Java identifiers.
  testing \p{Control} U+0015 NEGATIVE ACKNOWLEDGE...is LEGAL in Java identifiers.
  testing \p{Control} U+0016 SYNCHRONOUS IDLE...is LEGAL in Java identifiers.
  testing \p{Control} U+0017 END OF TRANSMISSION BLOCK...is LEGAL in Java identifiers.
  testing \p{Control} U+0018 CANCEL...is LEGAL in Java identifiers.
  testing \p{Control} U+0019 END OF MEDIUM...is LEGAL in Java identifiers.
  testing \p{Control} U+001A SUBSTITUTE...is LEGAL in Java identifiers.
  testing \p{Control} U+001B ESCAPE...is LEGAL in Java identifiers.
  testing \p{Control} U+001C INFORMATION SEPARATOR FOUR...is forbidden in Java identifiers.
  testing \p{Control} U+001D INFORMATION SEPARATOR THREE...is forbidden in Java identifiers.
  testing \p{Control} U+001E INFORMATION SEPARATOR TWO...is forbidden in Java identifiers.
  testing \p{Control} U+001F INFORMATION SEPARATOR ONE...is forbidden in Java identifiers.
  Legal but evil code points: U+0000 U+0001 U+0002 U+0003 U+0004 U+0005 U+0006 U+0007 U+0008 U+000E U+000F U+0010 U+0011 U+0012 U+0013 U+0014 U+0015 U+0016 U+0017 U+0018 U+0019 U+001A U+001B
  </code></pre>
  
  <p>And here is another demo:</p>
  
  <pre><code>$ perl test-java-idchars 0x600 0x700 | grep -i legal
  testing \p{Control} U+0600 ARABIC NUMBER SIGN...is LEGAL in Java identifiers.
  testing \p{Control} U+0601 ARABIC SIGN SANAH...is LEGAL in Java identifiers.
  testing \p{Control} U+0602 ARABIC FOOTNOTE MARKER...is LEGAL in Java identifiers.
  testing \p{Control} U+0603 ARABIC SIGN SAFHA...is LEGAL in Java identifiers.
  testing \p{Control} U+06DD ARABIC END OF AYAH...is LEGAL in Java identifiers.
  Legal but evil code points: U+0600 U+0601 U+0602 U+0603 U+06DD
  </code></pre>
  
  <h1>The Question</h1>
  
  <p>Can anyone please explain this seemingly insane behavior?  There are many, many, many other inexplicably permitted code points all over the place, starting right off with U+0000, which is perhaps the strangest of all.  If you run it on the first 0x1000 code points, you do see certain patterns appear, such as permitting any and all code points with the property <code>Current_Symbol</code>.  But too much else is wholly inexplicable, at least by me.</p>