Skip to content

Instantly share code, notes, and snippets.

@eakst7
Created April 22, 2022 11:32
Show Gist options
  • Save eakst7/2b4365cbca91905c9f9cb7f340794644 to your computer and use it in GitHub Desktop.
Save eakst7/2b4365cbca91905c9f9cb7f340794644 to your computer and use it in GitHub Desktop.
Demo of heap pollution via varargs parameters in Java
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