Skip to content

Instantly share code, notes, and snippets.

@alb-i986
Last active August 29, 2015 14:23
Show Gist options
  • Save alb-i986/dcdc682c9239457ffa5e to your computer and use it in GitHub Desktop.
Save alb-i986/dcdc682c9239457ffa5e to your computer and use it in GitHub Desktop.
Debuggers. Inspecting variables with self references
package debugger.test;
import org.junit.Test;
/**
* To show a "weird" behavior of Intellij IDEA and Eclipse debuggers.
*
* Here we have the class SelfObj that has a field that is a self reference.
*
* Set the breakpoint at the line shown, and inspect the variable selfObj.
* Expand the field of the variable.
* You will be able to expand it again. And again. And again... (recursion).
*
* This is something I found out while debugging some tests, and inspecting the exceptions.
* Throwable, in fact, has a cause that is by default a self reference.
*/
public class SelfRefDebuggerTest {
@Test
public void test() {
SelfObj selfObj = new SelfObj();
System.out.println(selfObj); // set a breakpoint at this line
// inspecting the var selfObj in the debugger
}
public static class SelfObj {
SelfObj self;
int integer = 10;
public SelfObj() {
this.self = this;
}
}
}
import java.lang.reflect.Field;
/**
* A fully working, yet simplistic, inspector of variables
* written for https://youtrack.jetbrains.com/issue/IDEABKL-7249
*
* It features a recursive function which loops over the fields of an object.
*
* The main point is this:
* when it encounters a variable with a field whose value is `this`,
* then it breaks the recursion (which would be an infinite loop, actually).
*
* @author alb-i986
*/
public class VarInspector {
private static final int ROOT_INDENT_LEVEL = 1;
public static void main(String[] args) throws IllegalAccessException {
drawVar(new SelfObj());
}
public static void drawVar(Object var) throws IllegalAccessException {
drawVarRecursive(var, ROOT_INDENT_LEVEL);
}
private static void drawVarRecursive(Object var, int level) throws IllegalAccessException {
printValueIndented(var, level - 1);
Field[] fields = var.getClass().getDeclaredFields();
// recursion base case
if (fields.length == 0) {
return;
}
for (Field field : fields) {
Object fieldValue = field.get(var);
// break the recursion if var has a field whose value is `this`
if (fieldValue == var) {
printFieldIndented(field, var, level);
continue;
}
// if field is an int, Field#get returns an Integer, so we need a special case
if (fieldValue instanceof Integer) {
printFieldIndented(field, var, level);
continue;
}
// more special cases should be added, at least one for each primitive type...
// ..but skipping for our simple case study
drawVarRecursive(fieldValue, level + 1);
}
}
private static void printFieldIndented(Field field, Object var, int level) throws IllegalAccessException {
Object fieldValue = field.get(var);
printIndented(field.getName() + ": " + String.valueOf(fieldValue), level);
}
private static void printValueIndented(Object value, int level) {
printIndented(String.valueOf(value), level);
}
private static void printIndented(String string, int level) {
String indent = "";
for (int i = 0 ; i<level ; i++) {
indent += " ";
}
System.out.println(indent + string);
}
public static class SelfObj {
SelfObj selfField = this;
int integerField = 1;
NonSelfObj nonSelfObjField = new NonSelfObj();
}
public static class NonSelfObj {
int numField = 2;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment