[java] 특정 유니 코드 문자로 주석에서 Java 코드를 실행할 수있는 이유는 무엇입니까?

다음 코드는 “Hello World!”출력을 생성합니다. (실제로 시도하십시오).

public static void main(String... args) {

   // The comment below is not a typo.
   // \u000d System.out.println("Hello World!");
}

Java 컴파일러는 유니 코드 문자 \u000d를 새 행으로 구문 분석하고 다음 과 같이 변환하기 때문입니다.

public static void main(String... args) {

   // The comment below is not a typo.
   //
   System.out.println("Hello World!");
}

따라서 주석이 “실행”됩니다.

이것은 악의적 인 코드 나 악의적 인 프로그래머가 생각할 수있는 것을 “숨기는”데 사용될 수 있기 때문에 왜 주석에 허용 됩니까?

Java 스펙에서 이것이 허용되는 이유는 무엇입니까?



답변

유니 코드 디코딩은 다른 어휘 변환 전에 발생합니다. 이것의 주요 이점은 ASCII와 다른 인코딩 사이를왔다 갔다하는 것이 사소한 것입니다. 주석이 어디서 시작하고 끝나는 지 알아낼 필요조차 없습니다!

JLS 섹션 3.3에 명시된 바와 같이 ASCII 기반 도구는 소스 파일을 처리 할 수 ​​있습니다.

[…] Java 프로그래밍 언어는 프로그램을 ASCII 기반 도구로 처리 할 수있는 형식으로 변경하는 유니 코드로 작성된 프로그램을 ASCII로 변환하는 표준 방법을 지정합니다. […]

이것은 항상 Java 플랫폼의 주요 목표였던 플랫폼 독립성 (지원되는 문자 세트의 독립성)을 근본적으로 보증합니다.

파일의 어느 곳에서나 유니 코드 문자를 작성할 수있는 기능은 깔끔한 기능이며, 특히 비 라틴 언어로 코드를 문서화 할 때 주석에서 중요합니다. 그것이 미묘한 방식으로 의미론을 방해 할 수 있다는 사실은 (불행한) 부작용입니다.

이 주제에는 많은 문제가 있으며 Joshua Bloch와 Neal Gafter의 Java Puzzlers 는 다음 변형을 포함했습니다.

이것은 합법적 인 Java 프로그램입니까? 그렇다면 무엇을 인쇄합니까?

\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020\u0020
\u0063\u006c\u0061\u0073\u0073\u0020\u0055\u0067\u006c\u0079
\u007b\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020
\u0020\u0020\u0020\u0020\u0073\u0074\u0061\u0074\u0069\u0063
\u0076\u006f\u0069\u0064\u0020\u006d\u0061\u0069\u006e\u0028
\u0053\u0074\u0072\u0069\u006e\u0067\u005b\u005d\u0020\u0020
\u0020\u0020\u0020\u0020\u0061\u0072\u0067\u0073\u0029\u007b
\u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074
\u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e\u0028\u0020
\u0022\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u0022\u002b
\u0022\u006f\u0072\u006c\u0064\u0022\u0029\u003b\u007d\u007d

(이 프로그램은 일반 “Hello World”프로그램으로 판명되었습니다.)

퍼즐 러에 대한 해결책에서 그들은 다음을 지적합니다.

더 진지하게,이 퍼즐은 이전 세 가지 교훈을 강화하는 데 도움이됩니다 . 유니 코드 이스케이프는 다른 방식으로 표현할 수없는 문자를 프로그램에 삽입해야 할 때 필수적입니다. 다른 모든 경우에는 피하십시오.


출처 : 자바 : 주석에서 코드를 실행하고 있습니까?!


답변

이것이 아직 다루어지지 않았으므로 여기에 설명, 왜 다른 소스 코드 처리 전에 유니 코드 이스케이프 변환이 발생합니까?

그 뒤에 아이디어는 다른 문자 인코딩간에 Java 소스 코드를 무손실로 번역 할 수 있다는 것입니다. 오늘날 유니 코드 지원은 널리 보급되어 있지만 문제처럼 보이지는 않지만 서구의 개발자가 아시아 문자를 포함하는 아시아 동료로부터 소스 코드를 수신하는 것은 쉽지 않았습니다. (컴파일 및 테스트 포함) 결과를 손상시키지 않고 결과를 다시 보냅니다.

따라서 Java 소스 코드는 모든 인코딩으로 작성 될 수 있으며 식별자, 문자 및 String리터럴 및 주석 내의 광범위한 문자를 허용합니다 . 그런 다음 무손실로 전송하기 위해 대상 인코딩에서 지원하지 않는 모든 문자는 유니 코드 이스케이프로 대체됩니다.

이것은 뒤집을 수있는 프로세스이며 흥미로운 점은 변환 규칙이 종속되지 않기 때문에 Java 소스 코드 구문에 대해 아무것도 알 필요가없는 도구로 변환을 수행 할 수 있다는 것입니다. 이것은 컴파일러 내부의 실제 유니 코드 문자로의 변환으로 Java 소스 코드 구문과 독립적으로 발생합니다. 소스 코드의 의미를 변경하지 않고 양방향으로 임의의 수의 변환 단계를 수행 할 수 있음을 의미합니다.

이것은 언급되지 않은 또 다른 이상한 기능의 이유입니다. \uuuuuuxxxx구문 :

변환 도구가 문자를 이스케이프하고 이미 이스케이프 된 시퀀스 인 시퀀스를 발견하면를 추가 u하여 시퀀스에 추가해야 \ucafe합니다 \uucafe. 의미는 변하지 않지만 다른 방향으로 변환 할 때 도구는 하나만 제거 u하고 단일 u문자를 포함하는 시퀀스 만 유니 코드 문자로 바꿔야합니다 . 이렇게하면 유니 코드 이스케이프도 앞뒤로 변환 할 때 원래 형식으로 유지됩니다. 아무도 그 기능을 사용한 적이 없다고 생각합니다…


답변

나는 나 자신을 도울 수 없기 때문에 완전히 비 효과적으로 포인트를 추가 할 것입니다. 아직 그것을 보지 못했습니다. 질문에 잘못된 전제가 포함되어 있기 때문에 질문이 유효하지 않다는 것입니다. 즉, 코드가 의견!

Java 소스 코드에서 \ u000d는 모든면에서 ASCII CR 문자와 동일합니다. 어디에서나 간단하고 끝이없는 줄입니다. 질문의 형식은 잘못된 것으로, 문자의 순서가 실제로 구문 상 일치하는 것은 다음과 같습니다.

public static void main(String... args) {
   // The comment below is no typo. 
   // 
 System.out.println("Hello World!");
}

IMHO의 가장 정확한 답은 다음과 같습니다. 코드가 주석에 없기 때문에 코드가 실행됩니다. 다음 줄에 있습니다. Java에서 “주석에서 코드 실행”은 예상 한대로 허용되지 않습니다.

혼동의 대부분은 구문 하이 라이터와 IDE가이 상황을 고려할만큼 정교하지 않다는 사실에서 비롯됩니다. 그들은 유니 코드 이스케이프를 전혀 처리하지 않거나 이전과 달리 코드를 구문 분석 한 후에 수행합니다 javac.


답변

\u000d때문에 탈출 코멘트를 종료 \u이스케이프가 균일하게 대응하는 유니 코드 문자로 변환 하기 전에 프로그램이 토큰 화됩니다. 주석 을 시작 하는 \u0057\u0057대신 동등하게 사용할 수 있습니다 .//

이것은 IDE의 버그이며 \u000d주석을 끝내는 것을 분명히하기 위해 줄을 구문 강조 표시해야합니다 .

언어의 디자인 오류이기도합니다. 이제는 수정할 수 없습니다. 그에 의존하는 프로그램이 손상 될 수 있습니다. \u이스케이프는 “이해되는”문맥 (문자열 리터럴 및 식별자, 다른 곳은 아님)에서만 컴파일러가 해당 유니 코드 문자로 변환하거나 U + 0000–007F 범위의 문자를 생성하는 것이 금지되어야합니다 , 아니면 둘다. 이러한 의미 중 하나가 이스케이프가 유용한 \u000d경우를 방해하지 않고 이스케이프에 의해 주석이 종료되는 것을 방지했을 것 입니다. 비 라틴 스크립트에서 주석을 인코딩하는 방법으로 주석 내에 이스케이프를 사용 \u하는 것이 포함됩니다\u . 텍스트 편집기는 더 넓은 위치를 볼 수 있습니다\u이스케이프는 컴파일러보다 중요합니다. ( 어쨌든 모든 컨텍스트 \u에서 이스케이프를 해당 문자로 표시하는 편집기 또는 IDE는 알지 못합니다 .)

C 계열에는 유사한 설계 오류가 있습니다. 1 주석 경계를 결정하기 전에 백 슬래시-줄 바꾸기가 처리됩니다. 예 :

// this is a comment \
   this is still in the comment!

나는이 특정 디자인 오류를 만들기가 쉽다는 것을 설명하기 위해 이것을 가져오고, 토큰 화에 대해 생각하고 컴파일러 프로그래머가 생각하는 방식을 파싱하는 데 익숙하다면 오류를 수정하기에 너무 늦을 때까지 오류라는 것을 깨닫지 못합니다. 토큰 화 및 파싱에 대해 기본적으로 공식 문법을 이미 정의한 경우 누군가 문법적인 특수 사례 (예 : 3 부작, 백 슬래시-줄 바꿈, ASCII로 제한되는 소스 파일에서 임의의 유니 코드 문자를 인코딩하는 등 무엇이든 포함)를 작성하는 것이 더 쉽습니다. 토크 나이저 앞에 변형 패스 추가 하여 토크 나이저를 재정 의하여 특수한 경우를 사용하는 것이 적절한 곳에주의를 기울이십시오.

1 pedants : 저는 C의이 측면이 100 % 의도적이라는 것을 알고 있습니다. 이론적 근거는 아닙니다. 필자는 이것을 펀치 카드에 임의로 긴 줄로 코드를 기계적으로 적용 할 수 있다는 것을 알고 있습니다. 여전히 잘못된 디자인 결정이었습니다.


답변

이것은 의도적 인 디자인 선택이었습니다. Java의 원래 디자인으로 거슬러 올라갑니다.

“코멘트에서 유니 코드 이스케이프를 원하는 사람?”을 묻는 사람들에게는 필자가 모국어가 라틴 문자 집합을 사용하는 사람들이라고 생각합니다. 다시 말해, 사람들은 Java 프로그램에서 합법적 인 곳이면 어디에서나 일반적으로 주석과 문자열로 임의의 유니 코드 문자를 사용할 수있는 Java의 원래 디자인에 내재되어 있습니다.

이러한 프로그램이 유니 코드 이스케이프를 해석 할 수없고 해당 글리프를 표시 할 수 없다는 소스 텍스트를 보는 데 사용되는 IDE (예 : IDE)와 같은 프로그램의 단점 일 수 있습니다.


답변

나는 이것이 @z 실수라는 것에 동의한다. 그러나 나는 훨씬 더 비판적입니다.

\u이스케이프는 문자열 및 문자 리터럴에 유용합니다. 그것이 존재해야하는 유일한 장소입니다. 다른 이스케이프와 같은 방식으로 처리해야합니다 \n. 그리고 "\u000A" 해야 정확히 의미한다 "\n".

\uxxxx의견을 말할 필요 는 없습니다 . 아무도 읽을 수 없습니다.

마찬가지로, \uxxxx프로그램의 다른 부분에서는 사용할 필요가 없습니다 . 유일한 예외는 아마도 비 ASCII 문자를 포함하도록 강제 된 퍼블릭 API 일 것입니다. 우리가 마지막으로 본 것은 무엇입니까?

디자이너들은 1995 년에 그 이유를 가지고 있었지만 20 년 후에는 잘못된 선택 인 것 같습니다.

(독자의 질문-왜이 질문이 계속 새로운 투표를 받습니까?이 질문은 다른 곳에서 연결 되었습니까?)


답변

유니 코드 이스케이프가 왜 사양을 작성했는지에 따라 구현 된 이유에 대해 대답 할 수있는 유일한 사람들입니다.

이에 대한 그럴듯한 이유는 전체 BMP를 Java 소스 코드의 가능한 문자로 허용하려는 요구가 있었기 때문입니다. 그래도 문제가 발생합니다.

  • 모든 BMP 문자를 사용할 수 있기를 원합니다.
  • BMP 캐릭터를 합리적으로 쉽게 입력 할 수 있기를 원합니다. 이를 수행하는 방법은 유니 코드 이스케이프를 사용하는 것입니다.
  • 사용자가 쉽게 읽고 쓸 수 있고 어휘도 구현하기 쉬운 어휘 사양을 유지하려고합니다.

이것은 유니 코드 이스케이프가 공격에 들어가면 엄청나게 어렵습니다. 새로운 어휘 규칙을 완전히 만듭니다.

쉬운 방법은 두 단계로 어휘를 수행하는 것입니다. 먼저 모든 유니 코드 이스케이프를 검색하고 나타내는 문자로 대체 한 다음 결과 문서를 유니 코드 이스케이프가 존재하지 않는 것처럼 구문 분석합니다.

이것의 장점은 쉽게 지정할 수 있기 때문에 사양이 더 간단 해지고 구현하기 쉽다는 것입니다.

단점은 예입니다.