12
\$\begingroup\$

Inspired by a PPCG answer, I wrote this code, which shuffles the autoboxing caches of Number subclasses.

This leads to, among other things, outputting 2 + 2 = -72 after invocation of EvilStuff.doEvilStuff(Class<? extends Number>,String,int). (The result differs for different invocations, due to reshuffling).

I use a standard Fisher-Yates shuffle. I use reflection to access the autoboxing caches by the designated cacheID and cacheFieldNumber parameters, which are cache and 0 for java.lang.Integer. java.lang.Integer is the only class which implements an effective autoboxing cache other than java.math.BigDecimal, and that is not used much for normal arithmetic. Also, the Integer stuff is better documented.

I figured I would do this since no one would otherwise think of posting evil code on Code Review, and to serve to warn myself of the dangers of using reflection in the future.

Welcome to the dark side! How would anyone else do this?

/**
 * Messes up arithmetic for some cases using reflection to shuffle cache stuffs. Enjoy!
 */
public class EvilStuff{
    /**
     * Does a Fisher-Yates shuffling of the input.
     * @param stuff The array to shuffle
     * @return Nothing, array is sorted in-place
     */
    public static void shuffle(Object[] stuff){
        int length = stuff.length;
        for (int i = 0; i < length; i++) {
            // Get a random index of the stuff past i.
            //truncation (flooring) is necessary to avoid array bounds violations
            int random = i + (int) (Math.random() * (length - i));
            // Swap the random element with the present element.
            Object randomElement = stuff[random];
            stuff[random] = stuff[i];
            stuff[i] = randomElement;
        }
    }

    /**
     * Access the "cache" field of a {@link java.lang.Number} subclass object,
     * and shuffle that cache.
     * 
     * Currently known to work only with {@link java.lang.Integer}
     * (See the special handling?)
     * 
     * Ridiculous output can only be seen if int (in the range of a {@link java.lang.Byte}
     * calculations are autoboxed to java.lang.Integer
     * 
     * Try {@link java.lang.System.out.format(String,Object...)} for best results!
     * 
     * @param victim The class whose cache should be shuffled. Recommend {@link java.lang.Integer}
     * @return Nothing, the victim should have been dealt with in-place
     */
    public static void doEvilStuff(Class<? extends Number> victim,String cacheID, int cacheFieldNumber){
        Class cache = victim.getDeclaredClasses()[cacheFieldNumber];
        java.lang.reflect.Field c;
        try{
            c = cache.getDeclaredField(cacheID);
        }catch(NoSuchFieldException noCache){
            throw new IllegalArgumentException(String.format("Can't mess with something (%s) without a cache (%s) at %d!"
                ,victim+"",cacheID,cacheFieldNumber), noCache);
        }
        c.setAccessible(true);
        Object cached;
        try{
            cached=c.get(cache);
        }catch(IllegalAccessException noAccessPermit){
            throw new IllegalArgumentException(String.format("Can't access the cache (%s) of %s at %d!",
                    cacheID,victim+"",cacheFieldNumber), noAccessPermit);
        }
        Number[] array;
        try{
            array = (Number[]) cached;
            shuffle(array);
        }catch(NullPointerException dataLoss){
            throw new IllegalArgumentException("Catastrophic data loss (for this program only)!", dataLoss);
        }
    }
}

A main class for testing purposes (DISCLAIMER: The following class not up for review):

public class TestEvilStuff {
    public static void main(String[] args) {
        EvilStuff.doEvilStuff(Integer.class, "cache", 0);
        System.out.println("Enter 2 numbers between -64 (-128/2) and 63 (127/2):");
        java.util.Scanner in = new java.util.Scanner(System.in);
        int n1=Integer.parseInt(in.nextLine()),
            n2=Integer.parseInt(in.nextLine());
        System.out.format("%d + %d = %d", new Integer(n1), new Integer(n2), n1 + n2);
    }
}

Sample outputs:

Enter 2 numbers between -64 (-128/2) and 63 (127/2):
63
64
63 + 64 = -94

Please find more and suggest how I can improve this. Maybe even make an EvilStuff library?

\$\endgroup\$
  • 3
    \$\begingroup\$ I, on behalf of PPCG, approve of this post. \$\endgroup\$ – Riker Mar 18 '16 at 17:06
  • \$\begingroup\$ @CaptainStupid I was just checking the help center to see if this is legal here (by legal, I imply on-topic). Apparently, it is. \$\endgroup\$ – Tamoghna Chowdhury Mar 18 '16 at 17:06
  • 3
    \$\begingroup\$ If this code successfully makes its client code not work as intended, then it's working as intended and is therefore on-topic.. no worries =) \$\endgroup\$ – Mathieu Guindon Mar 18 '16 at 17:13
4
\$\begingroup\$

Regarding shuffle:

  • Declare length in the for loop.
  • Use Random.random(int) instead of (int)(Math.random() * int).
  • Rename random to swapIndex or something more descriptive.
  • With these changes the comments are unnecessary.
  • This method needn't be public.
  • Also, I made things final although that's a matter of style.

My version:

private static void shuffle(final Object[] stuff){
    final Random r = new Random();
    for (int i = 0, length = stuff.length; i < length; i++) {
        final int swapIndex = i + r.nextInt(length - i);
        final Object randomElement = stuff[swapIndex];
        stuff[swapIndex] = stuff[i];
        stuff[i] = randomElement;
    }
}

Regarding doEvilStuff:

The proliferation of try/catches really clutters the code, especially because you rethrow an IllegalArgumentException each time. Your code would be more readable if you wrapped the whole method in a try/catch and used a multi-catch. Actually I would be controversial and advocate catching Exception because no matter what goes wrong, you want to wrap the exception and provide diagnostic information and it's impractical (and impossible without depending on implementation details) to list every exception that could be thrown. Note that catching Exception is bad in most circumstances.

However, with this approach of having just one catch block your error messages can't be quite as descriptive. That's okay as long as the thrown exception includes the parameters and the original exception - any more is IMO excessive.

Another issue with your code is it puts the burden of specifying the index of the nested class on the caller. As all nested classes of Number type X are named XCache, we may use this pattern in doEvilStuff to automatically find the correct nested class.

My version:

public static void doEvilStuff(Cfinal lass<? extends Number> victim,String cacheID) {
    try {
        final Class cache = Class.forName(victim.getName() + "$" + victim.getSimpleName() + "Cache");
        final java.lang.reflect.Field c = cache.getDeclaredField(cacheID);
        c.setAccessible(true);
        shuffle((Number[])c.get(cache));
    } catch (final Exception e) {
        throw new IllegalArgumentException("can't corrupt class " + victim, e);
    }
}
\$\endgroup\$
  • \$\begingroup\$ Thanks for the simplifying tip for the Number class. I leave it to the caller as the pattern may differ on different JDK implementations, e.g. Android. Maybe a default override would be in order? \$\endgroup\$ – Tamoghna Chowdhury Mar 18 '16 at 17:29
  • \$\begingroup\$ Also, I really like String.format(). \$\endgroup\$ – Tamoghna Chowdhury Mar 18 '16 at 17:30
  • \$\begingroup\$ Also, the humorous errors are part of the code :P, so forgive me for not taking your advice about the multi-catch, unless you can tell me how I can properly do it while keeping the error messages. +1 for the Number thing, anyway. \$\endgroup\$ – Tamoghna Chowdhury Mar 18 '16 at 17:32
  • \$\begingroup\$ @TamoghnaChowdhury Okay, feel free to keep those aspects of your code. I didn't mean to advocate concatenation instead of String.format() - it's just what I happened to use (and the difference is IMO negligible when there's just one value to insert). \$\endgroup\$ – Solomonoff's Secret Mar 18 '16 at 17:33
  • \$\begingroup\$ Regarding the shuffle stuff, it was a basic copy-paste, so some stuff got carried over. An oversight on my part, anyway. I keep it public as I might use it for other cases. I don't want to import java.util.Random for something this trivial (it doesn't save much except for a cast). However, the final s are a good idea. \$\endgroup\$ – Tamoghna Chowdhury Mar 18 '16 at 17:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.