Le Mystère
En explorant précisément les caractères qui ont été autorisés dans Java identifiants, j'ai trébuché sur quelque chose de très curieux qu'il semble presque certain d'être un bug.
Je m'attendais à trouver que Java identificateurs conformés à l'obligation de commencer avec des personnages qui ont la propriété Unicode ID_Start
et sont suivis par ceux qui ont la propriété ID_Continue
, avec une exception accordée pour les principaux traits de soulignement et des signes dollar. Qui ne s'avère pas être le cas, et ce que j'ai trouvé est à l'extrême de la variance ou de toute autre idée d'un identifiant que j'ai entendu parler de.
Courte Démo
Considérons l'exemple suivant prouve qu'un caractère ASCII ESC (octal 033) est autorisé en Java identifiants:
$ 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); }})' > escape.java
$ javac escape.java
$ java escape | cat -v
i am escape: ^[
C'est même pire que cela, cependant. Presque infiniment pire, en fait. Même les valeurs Null sont autorisées! Et des milliers d'autres points de code qui ne sont même pas identificateur de caractères. J'ai testé cela sur Solaris, Linux et Mac exécution de Darwin, et tous donnent les mêmes résultats.
Longue Démo
Voici un programme de test qui va vous montrer tous ces imprévus points de code Java assez outrageuosly permet dans le cadre d'un identificateur de nom.
#!/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 => ":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) || "<missing>") : "NULL";
next if $name eq "<missing>" && $cp > 0xFF;
my $msg = sprintf("U+%04X %s", $cp, $name);
print "testing \\p{$type} $msg...";
open(TESTPROGRAM, ">:utf8", "testchar.java") || die $!;
print TESTPROGRAM <<"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 \
&& \
java -Dfile.encoding=UTF-8 testchar | grep variable \
) >/dev/null 2>&1
};
push @legal, sprintf("U+%04X", $cp) if $? == 0;
if ($? && $? < 128) {
print "<interrupted>\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";
}
Voici un exemple d'exécution de ce programme sur la première 33 points de code qui ne sont ni espaces, ni caractères identifiant:
$ 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
Et voici une autre démonstration:
$ 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
La Question
Quelqu'un peut-il m'expliquer ce apparemment fous comportement? Il y a beaucoup, beaucoup, beaucoup d'autres inexplicablement permis points de code tous sur la place, à commencer tout de suite avec U+0000, qui est peut-être le plus étrange de tous. Si vous l'exécutez sur la première 0x1000 points de code, vous voyez certains modèles apparaissent, comme l'autorisation de tout et de tous les points de code de la propriété Current_Symbol
. Mais trop de choses est totalement inexplicable, au moins par moi.