2011-01-29 5 views
44

미스터리

자바 식별자에서 어떤 문자가 허용되었는지 정확하게 탐색하면서 매우 궁금한 점을 발견하여 버그라고 확신합니다.Java가 식별자에서 제어 문자를 허용하는 이유는 무엇입니까?

Java 식별자가 Unicode 속성이 ID_Start 인 문자로 시작하고 뒤에 ID_Continue 속성이있는 문자로 시작하는 요구 사항을 준수한다는 것을 알았 으면합니다. 단, 밑줄과 달러 기호는 예외입니다. 그게 사실이 아니라는 것을 알았고, 내가 발견 한 것은 정상적인 식별자에 대한 다른 아이디어 나 생각과는 극단적 인 차이가 있다는 것입니다.

짧은 데모

은 다음과 데모는 ASCII의 ESC 문자 (진수 033)은 자바 식별자에 허용되는 것을 증명 고려 :

$ 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: ^[ 

그것은하지만, 그것보다 더 나쁜입니다. 사실, 거의 무한히 악화됩니다. NULL도 허용됩니다! 그리고 심지어 식별 문자가 아닌 수천 개의 다른 코드 포인트. 필자는 솔라리스, 리눅스, 그리고 다윈을 운영하는 맥에서 이것을 시험해 보았습니다. 여기에 긴 데모

이 모든 예기치 않은 코드가 자바는 매우 outrageuosly 법적 식별자 이름의 일부로 수 있다는 지적이 표시됩니다 테스트 프로그램입니다. 또 다른 데모 여기

$ 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 

그리고있다 :

$ 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 

다음
#!/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 
# [email protected] 
# 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"; 
} 

는도 공백이나 식별자 문자 단지 첫 33 개 코드 포인트에 그 프로그램을 실행하는 샘플입니다 질문

누구나이 미친듯한 행동을 설명 할 수 있습니까? U + 0000으로 시작하는 곳곳에 많은 다른 많은 불가사의하게 허용 된 코드 포인트가 있습니다. 이것은 아마도 모두 이상한 것입니다. 처음 0x1000 코드 포인트에서 실행하면 Current_Symbol 속성으로 모든 코드 포인트를 허용하는 것과 같은 특정 패턴이 나타납니다. 그러나 너무 많은 다른 것들은 적어도 저로서는 설명 할 수 없습니다.

+0

사용중인 Java 언어 버전은 무엇입니까? IIRC 초기 버전은 "캐릭터가 몇 가지 특정 클래스 중 하나에 속하지 않으면 영숫자로 취급되고 식별자에 사용할 수 있습니다"라는 규칙을 가지고 있지만 이후 버전에서는 삭제 된 것으로 생각됩니다. – finnw

+2

이것은 정말 짜증나며, 정말 좋은 질문입니다. 나는 또한 왜 그들이 국가적인 특성을 식별자에 허용하는지 알지 못한다. 중국 식별자를 사용하는 경우 소스가 어떻게 생겼는지 상상해보십시오. 또는 일본어. 또는 오른쪽에서 왼쪽으로 쓰여진 히브리어. C#에서도 마찬가지입니다. 제 동료 중 한 명이 실제로 러시아어 식별자를 사용합니다. 그것은 끔찍한 것처럼 보입니다. 종종 러시아어 (자국어 포함)에 대해서도 마찬가지입니다. –

+0

@finnw : Java 버전 1.6입니다. – tchrist

답변

13

Java Language Specification section 3.8Character.isJavaIdentifierStart()Character.isJavaIdentifierPart()입니다. 후자는 다른 조건들 중에서도 Character.isIdentifierIgnorable()이며 공백이 아닌 제어 문자 (전체 C1 범위 포함, 목록 링크 참조)를 허용합니다.

+8

그래서 자바는 다시 유니 코드 표준에 맞지 않는 방식으로 자신의 고유 한 정의를 만들기로 결정했습니다. 나는 왜 ** - 대부분 ** ** - Unicode'Default_Ignorable_Code_Point' 속성이 그들의 신비한 목적을 위해 불충분하다는 것을 증명하고 왜 유니 코드와 모순되는 그들 자신의 정의를 만들어야 하는지를 알고 싶습니다. 자바가 유니 코드와 다른 화이트 스페이스라는 자체 개념을 가지고 있다는 점에서 의미가 있습니다. – tchrist

+2

U + 0602 ARABIC FOOTNOTE MARKER 및 U + 070F SYRIAC ABBREVIATION MARK와 U + 2062 INVISIBLE TIMES는 모두 Gc = Cf aka General_Category = Format과 같이 설명 할 수없는 코드 포인트를 설명하지 않습니다. 그것들은'\ w' 문자를 전혀 사용하지 않습니다! ** 보이지 않는 **은 어쨌든 무엇입니까? 그들은 무엇을 피우고 있습니까? 어떤 것이 중요하고 어떤 것이 무시할 수 있는지를 알 수 없습니다. 동일하지 않은 문자열은 동등한 것으로 테스트해서는 안됩니다. 대소 문자를 구별하지 않고 모든 모음이 동일한 지 테스트하는 식별자도 사용할 수 있습니다. 많은 (비) 감각을 갖습니다! – tchrist

+6

@tchrist : 나는 왜 "왜"에 대답하지 않았지만, 자바의 제작자 만이 그에 대한 확실한 답을 줄 수 있으므로, 아마도 여러분은 그것들에 접근하려고 노력해야한다. 나머지 우리. – ninjalj

8

또 다른 질문 일 수 있습니다 : Java가 식별자에 제어 문자를 허용해서는 안되는 이유는 무엇입니까?

언어 나 다른 시스템을 설계 할 때 좋은 원칙은 좋은 의도가없는 한 아무 것도 금지하지 않는 것입니다. 사용법을 모를 때가 지요. 구현 자와 사용자가 반대해야하는 규칙이 적을수록 좋습니다.

실제로 변수 이름에 이스케이프를 포함 시켜서이 기능을 사용하면 안되며 null 문자가있는 클래스를 표시하는 인기있는 라이브러리가 표시되지 않습니다.

틀림없이 이것은 악용 될 수 있지만 적절한 들여 쓰기 또는 잘 선택된 변수 이름을 강요하는 것보다 프로그래머를 이런 방식으로 스스로 보호하는 것이 언어 설계자의 임무는 아닙니다.

+7

@Avi : Java가 식별자에 제어 문자를 허용해서는 안되는 데에는 여러 가지 이유가 있습니다 !!시작하려면 제어 문자는 * ID_Start 문자가 아니며 ID_Continue 문자가 아니며 ** ** ** * Default_Ignorable_Code_Point 문자가 아닙니다. 또 다른 한개를 위해, 그들은 보이지 않는다. 그리고 3 분의 1, 이것은 단순한 엉성한 일입니다. 그것은 나쁜 디자인이다! 두 개의 동일하지 않은 ID가 같지 않더라도 동일한 것으로 처리됩니다. 이 얼마나 엉망 이냐! – tchrist

+4

우선 IS_Start와 ID_Continue AFAIK는 유니 코드 5.0의 부록 인 UAX # 31에 정의되었으며 Java 언어 구문이 정의 된 후에도 잘 정의되었습니다. 이 시점에서 자바의 합법적 인 문자로의 변경은 불필요한 이전 버전과 호환되지 않는 변경이었습니다. – Avi

+0

둘째로, 두 개의 동일하지 않은 식별자를 어떻게 동등하게 취급 할 수 있는지 잘 모르겠습니다. 당신이 가질 수있는 것은 두 가지 식별자가 동일하게 보이고 다르게 대우받는 것입니다. 그러나 문자와 숫자 및 수학 기호 만 허용하는 경우에도 마찬가지입니다. 예를 들어, 그리스 문자 mu (U + 03BC)는 수학 기호 micron (U + 00B5)과 동일하게 보입니다. – Avi

-2

큰 문제는 없습니다. 그것은 어쨌든 당신에게 어떤 영향을 미칩니 까?

개발자가 코드를 난독 화하려면 ASCII로 처리해야합니다.

개발자가 코드를 이해할 수 있도록하려면 업계의 언어 인 프랑크 (영어)를 사용합니다. 식별자는 ASCII뿐 아니라 일반적인 영어 단어에서도 가능합니다. 그렇지 않으면 아무도 자신의 코드를 사용하거나 읽지 않으며, 그가 좋아하는 미친 캐릭터를 사용할 수 있습니다.

+12

아이오와 주 할아버지에게 영어와 ASCII가 충분하다는이 아이디어는 뉴 델리의 라제 쉬에 더 좋을 것입니다. 아키라 교토 에선 큰 곤경에 처해있다. 그것은 제국주의적이고 겸손하다. 그것은 다른 사람들의 감수성보다 뛰어나다. 기본 문자 세트로 유니 코드를 사용하여 Java를 다시 사용할 수도 있으며, ASCII가 개선 된 것으로 간주되는 1960 년대로 복귀하기위한 로비도 있습니다. – tchrist

+2

유니 코드를 더 많이 볼수록 ASCII가 개선 될 수 있다는 느낌을 받게됩니다. 사람들이 자신의 민족을 사용하게하는 것은 외국인과 결코 협조하지 않는다면 좋은 생각 일 수 있습니다. 대부분의 사람들에게 그리스 문자 또는 심지어 가장 강조된 라틴 문자를 쓰는 방법이 없습니다. 대부분의 외국 문자는 대부분의 사람들이 읽을 수 없습니다. 그렇다면 프로그래밍 언어에서는 무엇이 좋은 것일까 요? – maaartinus

+2

뉴 델리의 라제 쉬 (Rajesh)는 매우 불쾌감을 느낍니다. 의사 소통 이외의 언어의 요지는 무엇입니까? 수백만 프로그래머가 모두 Java를 사용하는 이유는 무엇입니까? 왜 모두가 새로운 언어를 고안하지 않습니까? – irreputable

관련 문제