O código a seguir produz a saída "Hello World!" (realmente não, tente).
public static void main(String... args) {
// The comment below is not a typo.
// \u000d System.out.println("Hello World!");
}
A razão para isso é que o compilador Java analisa o caractere Unicode \u000d
como uma nova linha e é transformado em:
public static void main(String... args) {
// The comment below is not a typo.
//
System.out.println("Hello World!");
}
Assim, resultando em um comentário sendo "executado".
Como isso pode ser usado para "ocultar" códigos maliciosos ou o que um programador mal possa conceber, por que é permitido nos comentários ?
Por que isso é permitido pela especificação Java?
Respostas:
A decodificação Unicode ocorre antes de qualquer outra tradução lexical. O principal benefício disso é que torna trivial a alternância entre ASCII e qualquer outra codificação. Você nem precisa descobrir onde os comentários começam e terminam!
Conforme declarado na Seção 3.3 do JLS, isso permite que qualquer ferramenta baseada em ASCII processe os arquivos de origem:
Isso fornece uma garantia fundamental para a independência da plataforma (independência dos conjuntos de caracteres suportados), que sempre foi um objetivo principal da plataforma Java.
Ser capaz de escrever qualquer caractere Unicode em qualquer lugar do arquivo é um recurso interessante e especialmente importante nos comentários ao documentar o código em idiomas não latinos. O fato de poder interferir com a semântica de maneiras tão sutis é apenas um efeito colateral (infeliz).
Existem muitas dicas sobre esse tema e os Java Puzzlers de Joshua Bloch e Neal Gafter incluíram a seguinte variante:
(Este programa acaba sendo um programa simples "Hello World".)
Na solução para o quebra-cabeças, eles apontam o seguinte:
Fonte: Java: Executando código nos comentários ?!
fonte
\u000d
e a parte após ter destaque do código.// C:\user\...
que leva a um erro de compilação, pois\user
não é uma sequência de escape Unicode válida.\u000d
é destacado parcialmente. Depois de pressionar Ctrl + Shift + F o carácter é substituído com nova linha e o resto da linha é enrolada\u002A/
deve terminar o comentário.Como isso ainda não foi abordado, aqui está uma explicação de por que a tradução de Unicode escapa ocorre antes de qualquer outro processamento de código-fonte:
A idéia por trás disso era que ela permite traduções sem perdas do código-fonte Java entre diferentes codificações de caracteres. Hoje, há amplo suporte a Unicode, e isso não parece um problema, mas naquela época não era fácil para um desenvolvedor de um país ocidental receber algum código-fonte de seu colega asiático contendo caracteres asiáticos, fazer algumas alterações ( incluindo compilar e testá-lo) e enviar o resultado de volta, tudo sem danificar nada.
Portanto, o código-fonte Java pode ser escrito em qualquer codificação e permite uma ampla variedade de caracteres dentro de identificadores, caracteres e
String
literais e comentários. Em seguida, para transferi-lo sem perdas, todos os caracteres não suportados pela codificação de destino são substituídos por seus escapes Unicode.Esse é um processo reversível e o ponto interessante é que a tradução pode ser feita por uma ferramenta que não precisa saber nada sobre a sintaxe do código-fonte Java, pois a regra de tradução não depende dela. Isso funciona como a tradução para seus caracteres Unicode reais dentro do compilador também acontece independentemente da sintaxe do código-fonte Java. Isso implica que você pode executar um número arbitrário de etapas de tradução em ambas as direções sem alterar o significado do código-fonte.
Este é o motivo de outro recurso estranho que nem sequer mencionou: a
\uuuuuuxxxx
sintaxe:Quando uma ferramenta de tradução está escapando de caracteres e encontra uma sequência que já é uma sequência de escape, ela deve inserir um adicional
u
na sequência, convertendo\ucafe
para\uucafe
. O significado não muda, mas ao converter para outra direção, a ferramenta deve apenas remover umau
e substituir apenas as seqüências que contêm uma únicau
por seus caracteres Unicode. Dessa forma, até as fugas Unicode são mantidas em sua forma original ao converter para frente e para trás. Eu acho que ninguém nunca usou esse recurso ...fonte
native2ascii
não parece usar a\uu...xxxx
sintaxe,native2ascii
pretendia ajudar a preparar pacotes de recursos convertendo-os em iso-latin-1, comoProperties.load
foi corrigido para ler somente latin-1. E lá, as regras são diferentes, sem\uuu…
sintaxe e sem estágio inicial de processamento. Nos arquivos de propriedades,property=multi\u000aline
é realmente o mesmo queproperty=multi\nline
. (Contrariando a frase "usando escapes Unicode, conforme definido na seção 3.3 da especificação da linguagem Java ™" da documentação)\u
escapes para gerar caracteres no intervalo U + 0000–007F. (Todos esses caracteres podem ser representados de forma nativa por todas as codificações nacionais que foram relevantes na década de 1990, bem, talvez exceto alguns dos caracteres de controle, mas você não precisa aqueles para escrever Java qualquer maneira.)Vou acrescentar de maneira completamente ineficaz o argumento, só porque não posso me ajudar e ainda não o vi fazer, que a pergunta é inválida, pois contém uma premissa oculta que está errada, ou seja, que o código está dentro um comentário!
No código-fonte Java, \ u000d é equivalente em todos os aspectos a um caractere ASCII CR. É um final de linha, puro e simples, onde quer que ocorra. A formatação na pergunta é enganosa, ao que essa sequência de caracteres realmente sintaticamente corresponde é:
IMHO, a resposta mais correta é, portanto: o código é executado porque não está em um comentário; está na próxima linha. "Executar código nos comentários" não é permitido em Java, como você esperaria.
Grande parte da confusão decorre do fato de que marcadores de sintaxe e IDEs não são sofisticados o suficiente para levar essa situação em consideração. Eles não processam os escapes unicode ou o fazem depois de analisar o código em vez de antes, como o
javac
fazem.fonte
A
\u000d
fuga termina um comentário porque as\u
fugas são convertidas uniformemente nos caracteres Unicode correspondentes antes que o programa seja tokenizado. Você poderia igualmente usar\u0057\u0057
em vez de//
para começar um comentário.Este é um erro no seu IDE, que deve destacar a linha na sintaxe para deixar claro que o
\u000d
final do comentário é finalizado.Este também é um erro de design no idioma. Não pode ser corrigido agora, porque isso interromperia os programas que dependem dele.
\u
escapes devem ser convertidos no caractere Unicode correspondente pelo compilador apenas em contextos em que isso "faz sentido" (literais e identificadores de string e provavelmente em nenhum outro lugar) ou devem ter sido proibidos de gerar caracteres no intervalo U + 0000–007F , ou ambos. Qualquer uma dessas semânticas teria impedido que o comentário fosse encerrado pela\u000d
fuga, sem interferir nos casos em que\u
escapes são úteis - observe que isso inclui o uso de\u
escapes nos comentários como uma maneira de codificar comentários em um script não latino. editor de texto poderia ter uma visão mais ampla de onde\u
escapes são significativos do que o compilador. (Não conheço nenhum editor ou IDE que exiba\u
escapes como os caracteres correspondentes em qualquer contexto.)Há um erro de design semelhante na família C, 1 em que a barra invertida é processada antes que os limites do comentário sejam determinados, por exemplo,
Trago isso para ilustrar que é fácil cometer esse erro de design específico, e não percebo que é um erro até que seja tarde demais para corrigi-lo, se você está acostumado a pensar em tokenização e analisar a maneira como os programadores de compilador pensam sobre tokenização e análise. Basicamente, se você já definiu sua gramática formal e alguém aparece com um caso sintático especial - trigraphs, contrabarra de nova linha, codificando caracteres Unicode arbitrários em arquivos de origem limitados a ASCII, qualquer que seja - que precisam ser inseridos, é mais fácil adicione uma passagem de transformação antes do tokenizer para redefinir o tokenizer para prestar atenção ao local onde faz sentido usar esse caso especial.
1 Para pedantes: estou ciente de que esse aspecto de C era 100% intencional, com a lógica - não estou inventando isso - de que lhe permitiria aplicar mecanicamente o código de ajuste forçado com linhas arbitrariamente longas em cartões perfurados. Ainda foi uma decisão incorreta do projeto.
fonte
\u
foi menos absurda do que a decisão de seguir a liderança de C no uso de zeros à esquerda para notação octal. Embora a notação octal às vezes seja útil, ainda não ouvi ninguém articular um argumento sobre por que um zero à esquerda é uma boa maneira de indicá-lo.\u
como transformação de pré-tokenização se fosse proibido produzir caracteres na faixa U + 0000..U + 007F. É a combinação de "isso funciona em todos os lugares" e "esse nome alternativo de caracteres ASCII com significado sintático" que o despromete de algo estranho e totalmente errado.//
comentário em uma única linha não existia . E como C possui um terminador de instrução que não é uma nova linha, ele seria usado principalmente para cadeias longas, exceto que, até onde eu possa determinar, a " K&R " concatenação literal de cadeia " estava presente.Essa foi uma escolha de design intencional que remonta ao design original do Java.
Para aquelas pessoas que perguntam "quem quer que o Unicode escape nos comentários?", Presumo que sejam pessoas cujo idioma nativo usa o conjunto de caracteres latinos. Em outras palavras, é inerente ao design original do Java que as pessoas possam usar caracteres Unicode arbitrários onde quer que sejam legais em um programa Java, geralmente em comentários e strings.
É indiscutivelmente uma falha nos programas (como IDEs) usados para exibir o texto de origem que esses programas não podem interpretar as fugas de Unicode e exibir o glifo correspondente.
fonte
Concordo com @zwol que este é um erro de design; mas sou ainda mais crítico.
\u
escape é útil em literais de string e char; e esse é o único lugar que deveria existir. Deve ser tratado da mesma maneira que outras fugas, como\n
; e"\u000A"
deve significar exatamente"\n"
.Não há absolutamente nenhum ponto em ter
\uxxxx
comentários - ninguém pode ler isso.Da mesma forma, não há por que usar
\uxxxx
em outra parte do programa. A única exceção é provavelmente nas APIs públicas que são coagidas a conter alguns caracteres não-ascii - qual foi a última vez que vimos isso?Os designers tiveram suas razões em 1995, mas 20 anos depois, essa parece ser uma escolha errada.
(pergunta aos leitores - por que essa pergunta continua recebendo novos votos? esta questão está vinculada de algum lugar popular?)
fonte
int \u5431
quando você pode fazerint 整
UTF-8
apoio em 1995). Você só precisa chamar um método e não deseja instalar o pacote de suporte ao idioma asiático do seu sistema operacional (lembre-se, anos 90) para esse método único ...As únicas pessoas que podem responder por que as fugas Unicode foram implementadas como foram foram as pessoas que escreveram a especificação.
Uma razão plausível para isso é que havia o desejo de permitir todo o BMP como possíveis caracteres do código-fonte Java. Isso apresenta um problema:
Isso é incrivelmente difícil quando o escape do Unicode entra em conflito: ele cria uma carga completa de novas regras de lexer.
A saída mais fácil é fazer lexing em duas etapas: primeiro pesquise e substitua todos os escapes Unicode pelo caractere que representa e, em seguida, analise o documento resultante como se os escapes Unicode não existissem.
A vantagem disso é que é fácil especificar, por isso torna a especificação mais simples e fácil de implementar.
A desvantagem é, bem, o seu exemplo.
fonte