Magic Trick In Java
July 31, 2008 5:17 PMAfter reading a blog post about some of the quirks of the Java compiler and JVM, I thought I'd perform a magic trick for my colleagues to see how familiar they were with the same quirks.
The Trick
I have two hats and one ball. The ball looks like this:
Ball.java
public class Ball {
public static final String UNDER_HAT = "NONE";
}
The first hat looks like this:
HatOne.java
public class HatOne {
public boolean hasBall() {
return "ONE".equals(Ball.UNDER_HAT);
}
}
The second hat looks like this:
HatTwo.java
public class HatTwo {
public boolean hasBall() {
return "TWO".equals(Ball.UNDER_HAT);
}
}
The magic trick looks like this:
MagicTrick.java
public class MagicTrick {
public static void main(String... args) {
HatOne hatOne = new HatOne();
HatTwo hatTwo = new HatTwo();
if (hatOne.hasBall() && hatTwo.hasBall())
System.out.println("TADA!! The ball is under both hats!");
else if (hatOne.hasBall())
System.out.println("The ball is under hat one.");
else if (hatTwo.hasBall())
System.out.println("The ball is under hat two.");
else
System.out.println("Better luck next time.");
}
}
For this trick, I am not allowed to modify MagicTrick.java, HatOne.java or HatTwo.java, but I am allowed to change the implementation of Ball.java as much as I like. After making secret changes to Ball.java, I execute the magic trick:
$ java MagicTrick TADA!! The ball is under both hats!
How did I do it?
The Solution
If you consider yourself a seasoned Java developer, I suggest you have a go at finding a solution before reading this section, it will be fun. I expected that a number of my colleagues would work out how I did it, but what I didn't expect is that they would come up with 4 completely different ways of performing the trick.
Initially a solution was suggested that involved changing the package of some of the classes, but that meant editing the files, so that didn't count. Then a really simple solution arose that I hadn't thought of. It was suggested by Tom Davies, though with one small bug, which was fixed by Chris Mountford:
public class Ball {
static {
System.out.println("TADA!! The ball is under both hats!");
System.exit(0);
}
public static String UNDER_HAT = "NONE";
}
Well, that was too easy, so I changed the rules to say that the only class that was allowed to print any output was MagicTrick.java. Then came the solution that I was looking for, suggested by both Charles Miller and Chris Kiehl. It was:
- Compile all classes
- Modify Ball.java so UNDER_HAT="ONE"
- Compile Ball.java and HatOne.java
- Modify Ball.java so UNDER_HAT="TWO"
- Compile Ball.java and HatTwo.java
- Run MagicTrick - TADA!!
This solution exploits the way the Java compiler inlines static final fields wherever it can. The next solution, proposed by Chris Mountford, I thought someone might try. I don't think a magician would be very wise though to use this solution, it is greatly dependent on the speed of the processor, the OS scheduler, and whatever else the system happens to be doing at the same time:
public class Ball {
public static String UNDER_HAT = foo();
static String foo() {
Thread changer = new Thread() {
public void run() {
UNDER_HAT= "TWO";
}
};
changer.start();
try {
Thread.sleep(94);
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "ONE";
}
}
I thought that had to be all the solutions that would arise. But then, Matt Quail, a magician with a deep knowledge of the implementation of the String class and the way the JVM handles String literals, came up with this very elegant solution:
import java.lang.reflect.Field;
public class Ball {
public static Object UNDER_HAT;
static {
try {
new HatTwo().hasBall();
Field fValue = String.class.getDeclaredField("value");
fValue.setAccessible(true);
Field fOffset = String.class.getDeclaredField("offset");
fOffset.setAccessible(true);
Field fCount = String.class.getDeclaredField("count");
fCount.setAccessible(true);
String internedOne = "ONE";
String internedTwo = "TWO";
char[] none = {'N', 'O', 'N', 'E'};
UNDER_HAT = new String(none);
fValue.set(internedOne, none);
fOffset.setInt(internedOne, 0);
fCount.setInt(internedOne, 4);
fValue.set(internedTwo, none);
fOffset.setInt(internedTwo, 0);
fCount.setInt(internedTwo, 4);
} catch (Exception e) {
e.printStackTrace();
}
}
}
This solution exploits the way the JVM interns String literals, and changes the interned value of the String literals in the HatOne and HatTwo classes. Finally, I tried my hand at a solution that involved messing with class loaders:
import java.io.*;
public class Ball {
public static String UNDER_HAT = "ONE";
private static final String HAT_TWO = "cafebabe00000032000f0a0003000c07000d07000e0100063c696e69743e010003282956010004436f646501000f4c696e654e756d6265725461626c6501000768617342616c6c01000328295a01000a536f7572636546696c6501000b48617454776f2e6a6176610c0004000501000648617454776f0100106a6176612f6c616e672f4f626a656374002100020003000000000002000100040005000100060000001d00010001000000052ab70001b100000001000700000006000100000001000100080009000100060000001a000100010000000204ac000000010007000000060001000000010001000a00000002000b";
static {
try {
MagicTrickClassLoader mtcl = new MagicTrickClassLoader();
Class magicTrickClass = mtcl.loadClass("MagicTrick");
magicTrickClass.getMethod("main", new Class[] { String[].class })
.invoke(null, new Object[] { new String[] {} });
System.exit(0);
} catch (Exception e) {
}
}
private static class MagicTrickClassLoader extends ClassLoader {
public Class> loadClass(String name) throws ClassNotFoundException {
if ("MagicTrick".equals(name)) {
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
FileInputStream is = new FileInputStream(new File(
getClass().getResource(name + ".class").toURI()));
int b = is.read();
while (b >= 0) {
os..write(b);
b = is.read();
}
is.close();
byte[] bytes = os.toByteArray();
return super.defineClass(name, bytes, 0, bytes.length);
} catch (Exception e) {
}
} else if (name.matches("HatTwo")) {
byte[] bytes = new byte[HAT_TWO.length() / 2];
for (int i = 0; i < HAT_TWO.length(); i += 2) {
bytes[i / 2] = (byte) Integer.parseInt(HAT_TWO.substring(i, i + 2), 16);
}
return super.defineClass(name, bytes, 0, bytes.length);
}
return super.loadClass(name);
}
}
}
Initially embedding Java byte code in a Java source file felt a bit hairy to me, so I implemented it using the javax.tools API, but that required creating temporary directories and the like, this solution ended up being simpler (if you count less lines of code as being simpler). So, have we exhausted all the ways to perform this magic trick?



Copyright © 2009 Atlassian Pty Ltd.

23 Comment(s)
http://java.sun.com/javase/6/docs/api/java/lang/instrument/ClassFileTransformer.html
Transform java.lang.String.equals so that when invoked for a magic value, it checks the caller from stack, and responds as specified.
By Mikael Gueck at July 31, 2008 7:05 PM
I'm not sure that method will work without loading a Java agent (eg, java -javaagent:agent.jar). Doing that would give the magic trick away when executed. If you don't use a Java agent, then there is no way that I'm aware of to get a hold of an instance of an Instrumentation class, which means you can't add your ClassFileTransformer. Additionally, I thought Instrumentation.isModifiable(String.class) returned false, because the JVM won't let you modify class in the java.* package. However, you could transform the HatOne and HatTwo classes to always return true from hasBall.
By James Roper at July 31, 2008 7:25 PM
@James Roper -- you can load an instrumentation agent at runtime.. read how
By peter royal at July 31, 2008 9:31 PM
A fifth (sixth?) solution that doesn't even require modifying the Ball: http://www.papercut.com/blog/tom/2008/08/01/java-magic-trick-the-ball-is-everywhere/
By Tom Clift at August 1, 2008 2:07 AM
Great Puzzle James, but you are neglecting giving constraints.
Okay, your constraint is that the MagicTrick.java has to do the output.
If that is the only constraint I would make "java" a batch/shell on the path, which would for example put my custom classloader as a system property via "java.system.class.loader"
Then I could load custom Hat classes.
Also this could solve the javaagent command line option.
I guess you can always find a trick to make your own script/executable run instead of the original java one, doing any setup, bytecode modification you like.
Sure it misses the point of being a Java Trick, but that the kind of cheating magicians do :-)
By Fabian Lange at August 1, 2008 4:47 AM
I did consider making it so the only thing you could change was the value of UNDER_HAT, that would have eliminated a number of solutions because then a static block couldn't be placed inside Ball.java because it would never be initialised, due to the fact that UNDER_HAT is static final. But the good thing about having rules that aren't so strict is it increases the scope of creativity.
By James Roper at August 2, 2008 7:21 AM
If you changed the value of UNDER_HAT to be the return value of a static method (which could do the required meddling), would that still fall within the constraint?
By Chris Leggatt at August 5, 2008 10:35 PM
@Chris Leggatt
No, because Java inlines the result of the static method at compile time. That static method is only called once...
By Kit at August 6, 2008 8:14 PM
@Kit
Regardless of whether the static method is inlined or not, if it's used to initialise a static member it's only going to be used once to initialise that static particular static member.
What I was referring to was that while creating a static method that returns the value of "NONE" (while also changing the internal char array of the Strings "ONE" and "TWO" to be {'N', 'O', 'N', 'E'}) and assigning that to UNDER_HAT works, I was wondering whether this would violate the proposed constraint of "the only thing you could change was the value of UNDER_HAT". Technically, you still only changing the value of UNDER_HAT after all...
By Chris Leggatt at August 12, 2008 5:51 PM
The below required a wee small change to Ball impl and thus the how the UNDER_HAT value is retrieved.
public class Ball
{
public static String getHat() {return UNDER_HAT;}
/*
* Simple ball implementation.
* Remember the serial version ID to stop eclipse moaning and note the byte
* sequence of the class file header after CAFEBABE
* \u000C\u000A\u000F\u000E\u000B\u000A\u000B\u000E * \u002A\u002F \u0073\u0074\u0061\u0074\u0069\u0063\u007b\u0020
\u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074\u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e(
\u0022\u0054\u0041\u0044\u0041\u0021\u0021\u0020\u0054\u0068\u0065\u0020\u0062\u0061\u006c\u006c\u0020\u0069\u0073\u0020\u0075\u006e\u0064\u0065\u0072\u0020\u0062\u006f\u0074\u0068\u0020\u0068\u0061\u0074\u0073\u0021\u0022)
\u003b\u0020\u0053\u0079\u0073\u0074\u0065\u006d\u002e\u0065\u0078\u0069\u0074(\u0030)\u003b\u0020\u007d\u002F\u002A
*
*/
private static final String UNDER_HAT = "NONE";
}
By andy at November 19, 2008 6:52 AM
and of course I was being a muppet, left the string as final - change it to non-final and you can just use the ball class as:
public class Ball
{
/*
* Simple ball implementation.
* Remember the serial version ID to stop eclipse moaning and note the byte
* sequence of the class file header after CAFEBABE
* \u000C\u000A\u000F\u000E\u000B\u000A\u000B\u000E * \u002A\u002F \u0073\u0074\u0061\u0074\u0069\u0063\u007b\u0020
\u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074\u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e(
\u0022\u0054\u0041\u0044\u0041\u0021\u0021\u0020\u0054\u0068\u0065\u0020\u0062\u0061\u006c\u006c\u0020\u0069\u0073\u0020\u0075\u006e\u0064\u0065\u0072\u0020\u0062\u006f\u0074\u0068\u0020\u0068\u0061\u0074\u0073\u0021\u0022)
\u003b\u0020\u0053\u0079\u0073\u0074\u0065\u006d\u002e\u0065\u0078\u0069\u0074(\u0030)\u003b\u0020\u007d\u002F\u002A
* (c)Andy Shearman
*/
public static String UNDER_HAT = "NONE";
}
By andy at November 19, 2008 7:20 AM
I also messed with JVM's String cache and came up with this:
import java.lang.reflect.Field;
public class Ball {
public static String UNDER_HAT = "TWO";
static {
try {
Class stringClass = String.class;
Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
valueField.set("ONE", valueField.get(UNDER_HAT));
} catch (Throwable ignored) {}
}
}
By Homer at December 26, 2008 10:39 AM
Maybe I'm little bit late to the game, but I wrote my own solution here. I've also provided a detailed explanation, but if you do not bother reading it, I recommend skipping to the end where you can find a question, just to keep the game going :)
By kirlich at December 27, 2008 8:06 AM
Hi James,
how about this? ;)
public class Ball {
static boolean flip;
public static String UNDER_HAT = (flip=(!flip)) ? "ONE" : "TWO";
}
Jumper
Hungary
By Jumper at February 24, 2009 11:18 PM
What about AOP (www.eclipse.org/aspectj/).
Both advices achieve the same result.
The second doesn't modify the classes HatOne/-Two in any way.
It wouldn't be modifying Ball.java but adding an Aspect.
public aspect HackHat {
//Advice 1
boolean around() :
call (boolean HatOne.hasBall()) ||
call (boolean HatTwo.hasBall()) {
return true;
}
//Advice 2
boolean around() : call( boolean String.equals(Object)) {
return true;
}
}
By Damokles at March 22, 2009 11:31 AM
@Jumper: Did you try your solution? Do you understand why it does not work?
By Jesper at March 23, 2009 2:30 AM
@Homer: Give credit where credit is due. You ripped that from Dr. Heinz M. Kabutz.
http://www.javaspecialists.eu/archive/Issue014.html
By stolsvik at March 23, 2009 10:13 PM
How about
public static final String UNDER_HAT = "TWO".equals(Ball.UNDER_HAT)?"ONE":"TWO";
This does work.
By Rahul at March 24, 2009 4:24 AM
I love these sorts of puzzles, got any others?
By Ranman at April 18, 2009 2:56 AM
I published one just today on my personal blog (though, the way I've phrased it it's easy to know the answer before you try it):
http://jazzy.id.au/pebble/2009/04/24/java_concurrency_and_volatile.html
By James Roper at April 23, 2009 5:37 PM
Hi James,
I have another solution for your trick:
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
public class Ball {
static {
System.setOut(new PrintStream(new WriterOutputStream(new OutputStreamWriter(System.out, new Encoder()))));
}
public static String UNDER_HAT = "TWO";
static class WriterOutputStream extends OutputStream {
private Writer writer;
public WriterOutputStream(Writer writer) {
this.writer = writer;
}
public void write(int b) throws IOException {
writer.write(b);
}
public void flush() throws IOException {
writer.flush();
}
public void close() throws IOException {
writer.close();
}
}
static class Encoder extends CharsetEncoder {
private static final char EMPTY = '\u0000';
private char stack = EMPTY;
public Encoder() {
super(Charset.forName("UTF-8"), 1, 8);
}
@Override
protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
char c1, c2;
if (isStackEmpty()) {
c1 = in.get();
c2 = EMPTY;
} else {
c1 = pop();
c2 = in.get();
}
if (c1 == 'T') {
out.put("TADA!! T".getBytes());
} else if (c1 == 'h') {
if (c2 == EMPTY) {
push(c1);
} else if (c2 == 'a') {
out.put("both ha".getBytes());
} else {
out.put((byte)c1);
out.put((byte)c2);
}
} else if (c1 == 't') {
if (c2 == EMPTY) {
push(c1);
} else if (c2 == ' ') {
out.put("ts".getBytes());
}
} else if (c1 == '.') {
out.put((byte)'!');
return CoderResult.OVERFLOW;
} else if (c1 != 'o') {
out.put((byte)c1);
}
return CoderResult.UNDERFLOW;
}
private boolean isStackEmpty() {
return stack == EMPTY;
}
private void push(char c) {
if (!isStackEmpty()) {
throw new IllegalArgumentException("This stack does not support more than one character");
}
stack = c;
}
private char pop() {
char c = stack;
stack = EMPTY;
return c;
}
}
}
By Arne Limburg at July 7, 2009 1:58 AM
Interesting... what do you call that encoding?
By James Roper at July 7, 2009 2:59 AM
It is the UseOnlyInThisTrickEncoding ;-)
By Arne Limburg at July 7, 2009 11:51 PM