Created
April 22, 2022 11:32
-
-
Save eakst7/2b4365cbca91905c9f9cb7f340794644 to your computer and use it in GitHub Desktop.
Demo of heap pollution via varargs parameters in Java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.ibm.scrt.fvt; | |
/** | |
* This class is a demonstration of "potential heap pollution" via | |
* varargs parameters, and the @SafeVarargs annotation (and when it's | |
* safe to use). | |
*/ | |
public class HeapPollutionDemo { | |
static class Box<T> { | |
private final T v; | |
public Box(T v) { | |
this.v = v; | |
} | |
public T getValue() { | |
return v; | |
} | |
} | |
private Box<Integer>[] ints; | |
public final void safeVarargs1(Box<Integer>... myints) { | |
// This is safe (okay to use @SafeVarargs, though | |
// we haven't here). | |
// | |
// We never do anything that relies on | |
// the array elements being a Box<Integer> | |
for (Box<Integer> b : myints) { | |
System.out.println(b.getValue()); | |
} | |
} | |
@SafeVarargs | |
public final void safeVarargs2(Box<Integer>... myints) { | |
// This is safe. @SafeVarargs suppresses the warning | |
// on the myints parameter. | |
// | |
// We never do anything that relies on | |
// the array elements being a Box<Integer> | |
for (Box<Integer> b : myints) { | |
System.out.println(b.getValue()); | |
} | |
} | |
//@SafeVarargs | |
public final void unsafeVarargs1(Box<Integer>... myints) { | |
// This is unsafe (NOT okay to use @SafeVarargs) | |
// because we assume the array elements are actually | |
// boxing Integers and call the intValue() method. | |
for (Box<Integer> b : myints) { | |
System.out.println(b.getValue().intValue()); | |
} | |
} | |
//@SafeVarargs | |
public final void unsafeVarargs2(Box<Integer>... myints) { | |
// This is unsafe (NOT okay to use @SafeVarargs) | |
// because the input array is made visible via the | |
// instance variable "ints". | |
// | |
// A ClassCastException will not occur here, but can | |
// occur when we later access the ints array. | |
this.ints = myints; | |
} | |
public final void printInts() { | |
for (Box<Integer> i : ints) { | |
System.out.println(i.getValue().intValue()); | |
} | |
} | |
public void safe1() { | |
Box b1 = new Box<String>("abc"); | |
Box<Integer> b2 = new Box<Integer>(1); | |
// Even though we have an uncheck conversion here, | |
// and b1 is of the wrong type, we can still call | |
// the safeVarargs method. | |
safeVarargs1(b1, b2); | |
} | |
@SuppressWarnings({"rawtypes", "unchecked"}) | |
public void safe2() { | |
Box b1 = new Box<String>("abc"); | |
Box<Integer> b2 = new Box<Integer>(1); | |
// Even though we have an uncheck conversion here, | |
// and b1 is of the wrong type, we can still call | |
// the safeVarargs method. | |
// | |
// The SuppressWarnings annotation has suppressed | |
// the warnings on b1 and on the method call. | |
safeVarargs1(b1, b2); | |
} | |
@SuppressWarnings({"rawtypes", "unchecked"}) | |
public void safe3() { | |
Box b1 = new Box<String>("abc"); | |
Box<Integer> b2 = new Box<Integer>(1); | |
// Even though we have an uncheck conversion here, | |
// and b1 is of the wrong type, we can still call | |
// the safeVarargs method. | |
// | |
// The SuppressWarnings annotation has suppressed | |
// the warnings on b1 and on the method call. | |
safeVarargs2(b1, b2); | |
} | |
public void unsafe1() { | |
Box b1 = new Box<String>("abc"); | |
Box<Integer> b2 = new Box<Integer>(1); | |
// Calling an unsafe varargs method will throw | |
// a ClassCastException when accessing the value | |
// of Box b1 (which is a String) as an Integer. | |
unsafeVarargs1(b1, b2); | |
} | |
@SuppressWarnings({"rawtypes", "unchecked"}) | |
public void unsafe2() { | |
Box b1 = new Box<String>("abc"); | |
Box<Integer> b2 = new Box<Integer>(1); | |
// Calling an unsafe varargs method will throw | |
// a ClassCastException when accessing the value | |
// of Box b1 (which is a String) as an Integer. | |
// | |
// The SuppressWarnings annotation suppresses the | |
// warnings, but does nothing to avoid the | |
// ClassCastException | |
unsafeVarargs1(b1, b2); | |
} | |
public void unsafe3() { | |
Box b1 = new Box<String>("abc"); | |
Box<Integer> b2 = new Box<Integer>(1); | |
// Calling an unsafe varargs method will throw | |
// a ClassCastException when (sometime in a future | |
// method) accessing the value of Box b1 (which is | |
// a String) as an Integer. | |
unsafeVarargs2(b1, b2); | |
} | |
public void run() { | |
safe1(); | |
safe2(); | |
safe3(); | |
try { | |
unsafe1(); | |
throw new RuntimeException("Cannot happen"); | |
} catch (ClassCastException e) { | |
e.printStackTrace(); | |
} | |
try { | |
unsafe2(); | |
throw new RuntimeException("Cannot happen"); | |
} catch (ClassCastException e) { | |
e.printStackTrace(); | |
} | |
unsafe3(); // Unsafe, but exception occurrs later. | |
try { | |
printInts(); | |
throw new RuntimeException("Cannot happen"); | |
} catch (ClassCastException e) { | |
e.printStackTrace(); | |
} | |
System.out.println("Successful demonstration"); | |
} | |
public static void main(String[] args) { | |
HeapPollutionDemo mytest = new HeapPollutionDemo(); | |
mytest.run(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment