/* Copyright (C) 2004 Free Software Foundation This file is part of libgcj. This software is copyrighted work licensed under the terms of the Libgcj License. Please consult the file "LIBGCJ_LICENSE" for details. */ /* Utility methods that allow an object to be converted to a textual representation on an OutputStream. The intention here is that this class be used for debugging, so we provide information about all fields, public or otherwise. */ package gnu.gcj.util; import java.lang.reflect.*; import java.io.*; import java.util.*; class Debug { private final PrintStream p; private final int maxdepth; private final int maxArrayLength; private final boolean printStaticFields; private int depth; Debug(PrintStream writer, int maxdepth, int maxArrayLength, boolean printStaticFields) { p = writer; this.maxdepth = maxdepth; this.maxArrayLength = maxArrayLength; this.printStaticFields = printStaticFields; } Debug(PrintStream writer) { this(writer, 0, 10, false); } Debug(int maxdepth, boolean printStaticFields) { this(new PrintStream (new FileOutputStream(FileDescriptor.err), true), maxdepth, maxdepth > 0 ? 1000 : 10, printStaticFields); } Debug(int maxdepth) { this(maxdepth, false); } Debug() { this(0, false); } private final void indent() { for (int i = 0; i < depth; i++) p.print(" "); } private final java.util.IdentityHashMap h = new java.util.IdentityHashMap(); private static native Field[] getDeclaredFields(Class c); private static native Object getField(Object o, Field f); private static native long getAddr(Object o); // Return an array containing all the fields of a class and its // superclasses. private Field[] internalGetFields(Class c) { HashSet set = new HashSet(); set.addAll(Arrays.asList(getDeclaredFields(c))); Class[] interfaces = c.getInterfaces(); for (int i = 0; i < interfaces.length; i++) set.addAll(Arrays.asList(internalGetFields(interfaces[i]))); Class superClass = c.getSuperclass(); if (superClass != null) set.addAll(Arrays.asList(internalGetFields(superClass))); return (Field[])set.toArray(new Field[set.size()]); } // FIXME: We could just use getClass() here, but this is a // workaround for a C++ bug that is causing getClass() to be // miscompiled. static private Class getItsClass(Object O) { return O.getClass(); } // Print a reasonably readable textual representation of an object // on our OutputStream. Objects are only printed once, no matter // how many references point to them. private void print(Object O) { int savedDepth = depth; h.put(O, O); try { Class C = getItsClass(O); p.print(C.getName() + "@"); p.println(Long.toHexString(getAddr(O))); if (C.isArray()) { indent(); p.println("{"); depth++; indent(); C = C.getComponentType(); int len = Array.getLength(O); for (int i = 0; i < len; i++) { Object thing = Array.get(O, i); print0(thing, C); p.print(", "); if (i > maxArrayLength) { p.print("..."); break; } } depth--; p.println(); indent(); p.print("}"); return; } indent(); p.println("{"); depth++; if (C == java.lang.Class.class) { indent(); p.println ("class = " + O.toString() + ","); } else if (C == java.lang.reflect.Field.class) { indent(); p.println (" = \"" + O.toString() + "\","); } else if (C == java.lang.String.class) { indent(); p.println (" = \"" + O.toString() + "\","); } Field[] f = internalGetFields(C); for (int i = 0; i < f.length; i++) { Class type = f[i].getType(); boolean isStatic = (f[i].getModifiers() & Modifier.STATIC) != 0; if (isStatic && ! printStaticFields) continue; indent(); if (isStatic) p.print("static "); p.print(type.getName() +" " +f[i].getName() + " = "); Object thing = getField(O, f[i]); print0(thing, type); p.println(","); } depth--; indent(); p.print("}"); } catch (Throwable t) { p.print("error: 0x" + Long.toHexString(getAddr(O)) + ";"); depth = savedDepth; } } private void print0(Object thing, Class C) { try { if (thing == null) { p.print("null"); return; } else if (C == gnu.gcj.RawData.class || C == gnu.gcj.RawDataManaged.class) { } else if (C.isPrimitive()) { if (getItsClass(thing) == Character.class) p.print("'" + thing + "'"); else p.print(thing); return; } else if (getItsClass(thing) == String.class) { p.print("\"" + thing + "\""); return; } else if (depth < maxdepth && h.get(thing) == null) { depth++; print(thing); depth--; return; } } catch (Throwable t) { } // The default action: just print the address. p.print("0x"+ Long.toHexString(getAddr(thing))); } // Print the textual representation of an object on System.err. public void write(Object O) { depth = 0; print(O); p.flush(); } }