Há alguns anos, encontrei um vídeo do podcaster Lex Friedman em que ele demonstra que é possível fazer 2+2 ser cinco em Java usando alguns truques.
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws Exception {
Class cache = Integer.class.getDeclaredClasses()[0];
Field c = cache.getDeclaredField("cache");
c.setAccessible(true);
Integer[] array = (Integer[]) c.get(cache);
array[132] = array[133];
System.out.printf("%d",2 + 2);
}
}
Por curiosidade, resolvi rodar o código disponibilizado nos links do vídeo e me deparei com o seguinte erro:
Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make field static final java.lang.Integer[] java.lang.Integer$IntegerCache.cache accessible: module java.base does not "opens java.lang" to unnamed module @8efb846
at java.base/java.lang.reflect.AccessibleObject.throwInaccessibleObjectException(AccessibleObject.java:391)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:367)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:315)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:183)
at java.base/java.lang.reflect.Field.setAccessible(Field.java:177)
at Old2Plus2.main(Old2Plus2.java:7)
Percebi que, para as novas versões do Java, esse hack não era mais possível. Portanto, neste artigo, irei demonstrar como realizar esse truque para o Java 21
A mensagem de erro diz que o módulo java.base não está aberto. Isso significa que não é possível usar reflexão a partir da classe Main criada para esse exemplo. Caso não tenha muita familiaridade com módulos, recomendo dar uma relembrada.
Ou seja, no Java 21, é mais difícil fazer esse tipo de hack com a linguagem. Felizmente, existe uma classe que nos permite negligenciar o sistema de módulos: a sun.misc.Unsafe. Essa classe é utilizada para executar operações de baixo nível e não é recomendada para o dia a dia de programadores comuns.
Sendo assim, a nova versão do código que faz 2+2 = 5 ficaria dessa forma:
import java.lang.reflect.Field;
public class New2Plus2 {
public static void main(String[] args) throws Exception {
Class usf = Class.forName("sun.misc.Unsafe");//pega o objeto que representa a classe Unsafe
Field unsafeField = usf.getDeclaredField("theUnsafe");//pega o campo theUnsafe da classe Unsafe
unsafeField.setAccessible(true);//define o campo theUnsafe como público
sun.misc.Unsafe unsafe = (sun.misc.Unsafe)unsafeField.get(null);//pega o valor do campo theUnsafe, como ele é static é passado o parâmetro null
Class<?> clazz = Class.forName("java.lang.Integer$IntegerCache");// pega o objeto que representa a classe IntegerCache
Field field = clazz.getDeclaredField("cache");//pega o campo cache da classe IntegerCache
Integer[] cache = (Integer[])unsafe.getObject(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field));//utiliza a classe Unsafe para pegar o cache de Integer
cache[132] = cache[133];// troca o valor 4 pelo 5
System.out.printf("2+2 = %d",2 + 2);
}
}
Executando o código acima, você verá que o resultado da soma será 5. Obviamente, não recomendo utilizar esse código em produção se você quiser manter sua sanidade e seu emprego.
Referências:
https://github.com/joao9aulo/2plus2equals5
https://javax0.wordpress.com/2017/05/03/hacking-the-integercache-in-java-9/
https://www.youtube.com/watch?v=amXXYgu0eFY
https://www.oracle.com/br/corporate/features/understanding-java-9-modules.html
Top comments (2)
A nova versão do hack deu certinho!
Contudo, a versão anterior ainda funciona sem maiores problemas, vc só precisa usar as opções certas na hora de executar o programa.
A mensagem de erro diz que o módulo
java.base
não abre o pacotejava.lang
para o seu módulo sem nome.Por isso precisamos usar a opção
--add-opens
. Esta opção permite sobrescrever as definições do módulo. O comando é o seguinte:Boa!