--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/tools/hat/resources/hat.js Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1475 @@
+/*
+ * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+
+/*
+ * The contents of this file are subject to the Sun Public License
+ * Version 1.0 (the "License"); you may not use this file except in
+ * compliance with the License. A copy of the License is available at
+ * http://www.sun.com/, and in the file LICENSE.html in the
+ * doc directory.
+ *
+ * The Original Code is HAT. The Initial Developer of the
+ * Original Code is Bill Foote, with contributions from others
+ * at JavaSoft/Sun. Portions created by Bill Foote and others
+ * at Javasoft/Sun are Copyright (C) 1997-2004. All Rights Reserved.
+ *
+ * In addition to the formal license, I ask that you don't
+ * change the history or donations files without permission.
+ *
+ */
+
+var hatPkg = Packages.com.sun.tools.hat.internal;
+
+/**
+ * This is JavaScript interface for heap analysis using HAT
+ * (Heap Analysis Tool). HAT classes are refered from
+ * this file. In particular, refer to classes in hat.model
+ * package.
+ *
+ * HAT model objects are wrapped as convenient script objects so that
+ * fields may be accessed in natural syntax. For eg. Java fields can be
+ * accessed with obj.field_name syntax and array elements can be accessed
+ * with array[index] syntax.
+ */
+
+// returns an enumeration that wraps elements of
+// the input enumeration elements.
+function wrapperEnumeration(e) {
+ return new java.util.Enumeration() {
+ hasMoreElements: function() {
+ return e.hasMoreElements();
+ },
+ nextElement: function() {
+ return wrapJavaValue(e.nextElement());
+ }
+ };
+}
+
+// returns an enumeration that filters out elements
+// of input enumeration using the filter function.
+function filterEnumeration(e, func, wrap) {
+ var next = undefined;
+ var index = 0;
+
+ function findNext() {
+ var tmp;
+ while (e.hasMoreElements()) {
+ tmp = e.nextElement();
+ index++;
+ if (wrap) {
+ tmp = wrapJavaValue(tmp);
+ }
+ if (func(tmp, index, e)) {
+ next = tmp;
+ return;
+ }
+ }
+ }
+
+ return new java.util.Enumeration() {
+ hasMoreElements: function() {
+ findNext();
+ return next != undefined;
+ },
+
+ nextElement: function() {
+ if (next == undefined) {
+ // user may not have called hasMoreElements?
+ findNext();
+ }
+ if (next == undefined) {
+ throw "NoSuchElementException";
+ }
+ var res = next;
+ next = undefined;
+ return res;
+ }
+ };
+}
+
+// enumeration that has no elements ..
+var emptyEnumeration = new java.util.Enumeration() {
+ hasMoreElements: function() {
+ return false;
+ },
+ nextElement: function() {
+ throw "NoSuchElementException";
+ }
+ };
+
+function wrapRoot(root) {
+ if (root) {
+ return {
+ id: root.idString,
+ description: root.description,
+ referrer: wrapJavaValue(root.referer),
+ type: root.typeName
+ };
+ } else {
+ return null;
+ }
+}
+
+function JavaClassProto() {
+ function jclass(obj) {
+ return obj['wrapped-object'];
+ }
+
+ // return whether given class is subclass of this class or not
+ this.isSubclassOf = function(other) {
+ var tmp = jclass(this);
+ var otherid = objectid(other);
+ while (tmp != null) {
+ if (otherid.equals(tmp.idString)) {
+ return true;
+ }
+ tmp = tmp.superclass;
+ }
+ return false;
+ }
+
+ // return whether given class is superclass of this class or not
+ this.isSuperclassOf = function(other) {
+ return other.isSubclassOf(this);
+ }
+
+ // includes direct and indirect superclasses
+ this.superclasses = function() {
+ var res = new Array();
+ var tmp = this.superclass;
+ while (tmp != null) {
+ res[res.length] = tmp;
+ tmp = tmp.superclass;
+ }
+ return res;
+ }
+
+ /**
+ * Returns an array containing subclasses of this class.
+ *
+ * @param indirect should include indirect subclasses or not.
+ * default is true.
+ */
+ this.subclasses = function(indirect) {
+ if (indirect == undefined) indirect = true;
+ var classes = jclass(this).subclasses;
+ var res = new Array();
+ for (var i in classes) {
+ var subclass = wrapJavaValue(classes[i]);
+ res[res.length] = subclass;
+ if (indirect) {
+ res = res.concat(subclass.subclasses());
+ }
+ }
+ return res;
+ }
+ this.toString = function() { return jclass(this).toString(); }
+}
+
+var theJavaClassProto = new JavaClassProto();
+
+// Script wrapper for HAT model objects, values.
+// wraps a Java value as appropriate for script object
+function wrapJavaValue(thing) {
+ if (thing == null || thing == undefined ||
+ thing instanceof hatPkg.model.HackJavaValue) {
+ return null;
+ }
+
+ if (thing instanceof hatPkg.model.JavaValue) {
+ // map primitive values to closest JavaScript primitives
+ if (thing instanceof hatPkg.model.JavaBoolean) {
+ return thing.toString() == "true";
+ } else if (thing instanceof hatPkg.model.JavaChar) {
+ return thing.toString() + '';
+ } else {
+ return java.lang.Double.parseDouble(thing.toString());
+ }
+ } else {
+ // wrap Java object as script object
+ return wrapJavaObject(thing);
+ }
+}
+
+// wrap Java object with appropriate script object
+function wrapJavaObject(thing) {
+
+ // HAT Java model object wrapper. Handles all cases
+ // (instance, object/primitive array and Class objects)
+ function javaObject(jobject) {
+ // FIXME: Do I need this? or can I assume that these would
+ // have been resolved already?
+ if (jobject instanceof hatPkg.model.JavaObjectRef) {
+ jobject = jobject.dereference();
+ if (jobject instanceof hatPkg.model.HackJavaValue) {
+ print(jobject);
+ return null;
+ }
+ }
+
+ if (jobject instanceof hatPkg.model.JavaObject) {
+ return new JavaObjectWrapper(jobject);
+ } else if (jobject instanceof hatPkg.model.JavaClass) {
+ return new JavaClassWrapper(jobject);
+ } else if (jobject instanceof hatPkg.model.JavaObjectArray) {
+ return new JavaObjectArrayWrapper(jobject);
+ } else if (jobject instanceof hatPkg.model.JavaValueArray) {
+ return new JavaValueArrayWrapper(jobject);
+ } else {
+ print("unknown heap object type: " + jobject.getClass());
+ return jobject;
+ }
+ }
+
+ // returns wrapper for Java instances
+ function JavaObjectWrapper(instance) {
+ var things = instance.fields;
+ var fields = instance.clazz.fieldsForInstance;
+
+ // instance fields can be accessed in natural syntax
+ return new JSAdapter() {
+ __getIds__ : function() {
+ var res = new Array(fields.length);
+ for (var i in fields) {
+ res[i] = fields[i].name;
+ }
+ return res;
+ },
+ __has__ : function(name) {
+ for (var i in fields) {
+ if (name == fields[i].name) return true;
+ }
+ return name == 'class' || name == 'toString' ||
+ name == 'wrapped-object';
+ },
+ __get__ : function(name) {
+
+ for (var i in fields) {
+ if(fields[i].name == name) {
+ return wrapJavaValue(things[i]);
+ }
+ }
+
+ if (name == 'class') {
+ return wrapJavaValue(instance.clazz);
+ } else if (name == 'toString') {
+ return function() {
+ return instance.toString();
+ }
+ } else if (name == 'wrapped-object') {
+ return instance;
+ }
+
+ return undefined;
+ }
+ }
+ }
+
+
+ // return wrapper for Java Class objects
+ function JavaClassWrapper(jclass) {
+ var fields = jclass.statics;
+
+ // to access static fields of given Class cl, use
+ // cl.statics.<static-field-name> syntax
+ this.statics = new JSAdapter() {
+ __getIds__ : function() {
+ var res = new Array(fields.length);
+ for (var i in fields) {
+ res[i] = fields[i].field.name;
+ }
+ return res;
+ },
+ __has__ : function(name) {
+ for (var i in fields) {
+ if (name == fields[i].field.name) {
+ return true;
+ }
+ }
+ return theJavaClassProto[name] != undefined;
+ },
+ __get__ : function(name) {
+ for (var i in fields) {
+ if (name == fields[i].field.name) {
+ return wrapJavaValue(fields[i].value);
+ }
+ }
+ return theJavaClassProto[name];
+ }
+ }
+
+ if (jclass.superclass != null) {
+ this.superclass = wrapJavaValue(jclass.superclass);
+ } else {
+ this.superclass = null;
+ }
+
+ this.loader = wrapJavaValue(jclass.getLoader());
+ this.signers = wrapJavaValue(jclass.getSigners());
+ this.protectionDomain = wrapJavaValue(jclass.getProtectionDomain());
+ this.instanceSize = jclass.instanceSize;
+ this.name = jclass.name;
+ this.fields = jclass.fields;
+ this['wrapped-object'] = jclass;
+ this.__proto__ = this.statics;
+ }
+
+ // returns wrapper for Java object arrays
+ function JavaObjectArrayWrapper(array) {
+ var elements = array.elements;
+ // array elements can be accessed in natural syntax
+ // also, 'length' property is supported.
+ return new JSAdapter() {
+ __getIds__ : function() {
+ var res = new Array(elements.length);
+ for (var i = 0; i < elements.length; i++) {
+ res[i] = i;
+ }
+ return res;
+ },
+ __has__: function(name) {
+ return (typeof(name) == 'number' &&
+ name >= 0 && name < elements.length) ||
+ name == 'length' || name == 'class' ||
+ name == 'toString' || name == 'wrapped-object';
+ },
+ __get__ : function(name) {
+ if (typeof(name) == 'number' &&
+ name >= 0 && name < elements.length) {
+ return wrapJavaValue(elements[name]);
+ } else if (name == 'length') {
+ return elements.length;
+ } else if (name == 'class') {
+ return wrapJavaValue(array.clazz);
+ } else if (name == 'toString') {
+ return function() { return array.toString(); }
+ } else if (name == 'wrapped-object') {
+ return array;
+ } else {
+ return undefined;
+ }
+ }
+ }
+ }
+
+ // returns wrapper for Java primitive arrays
+ function JavaValueArrayWrapper(array) {
+ var type = String(java.lang.Character.toString(array.elementType));
+ var elements = array.elements;
+ // array elements can be accessed in natural syntax
+ // also, 'length' property is supported.
+ return new JSAdapter() {
+ __getIds__ : function() {
+ var r = new Array(array.length);
+ for (var i = 0; i < array.length; i++) {
+ r[i] = i;
+ }
+ return r;
+ },
+ __has__: function(name) {
+ return (typeof(name) == 'number' &&
+ name >= 0 && name < array.length) ||
+ name == 'length' || name == 'class' ||
+ name == 'toString' || name == 'wrapped-object';
+ },
+ __get__: function(name) {
+ if (typeof(name) == 'number' &&
+ name >= 0 && name < array.length) {
+ return elements[name];
+ }
+
+ if (name == 'length') {
+ return array.length;
+ } else if (name == 'toString') {
+ return function() { return array.valueString(true); }
+ } else if (name == 'wrapped-object') {
+ return array;
+ } else if (name == 'class') {
+ return wrapJavaValue(array.clazz);
+ } else {
+ return undefined;
+ }
+ }
+ }
+ }
+ return javaObject(thing);
+}
+
+// unwrap a script object to corresponding HAT object
+function unwrapJavaObject(jobject) {
+ if (!(jobject instanceof hatPkg.model.JavaHeapObject)) {
+ try {
+ jobject = jobject["wrapped-object"];
+ } catch (e) {
+ print("unwrapJavaObject: " + jobject + ", " + e);
+ jobject = undefined;
+ }
+ }
+ return jobject;
+}
+
+/**
+ * readHeapDump parses a heap dump file and returns script wrapper object.
+ *
+ * @param file Heap dump file name
+ * @param stack flag to tell if allocation site traces are available
+ * @param refs flag to tell if backward references are needed or not
+ * @param debug debug level for HAT
+ * @return heap as a JavaScript object
+ */
+function readHeapDump(file, stack, refs, debug) {
+
+ // default value of debug is 0
+ if (!debug) debug = 0;
+
+ // by default, we assume no stack traces
+ if (!stack) stack = false;
+
+ // by default, backward references are resolved
+ if (!refs) refs = true;
+
+ // read the heap dump
+ var heap = hatPkg.parser.HprofReader.readFile(file, stack, debug);
+
+ // resolve it
+ heap.resolve(refs);
+
+ // wrap Snapshot as convenient script object
+ return wrapHeapSnapshot(heap);
+}
+
+/**
+ * The result object supports the following methods:
+ *
+ * forEachClass -- calls a callback for each Java Class
+ * forEachObject -- calls a callback for each Java object
+ * findClass -- finds Java Class of given name
+ * findObject -- finds object from given object id
+ * objects -- returns all objects of given class as an enumeration
+ * classes -- returns all classes in the heap as an enumeration
+ * reachables -- returns all objects reachable from a given object
+ * livepaths -- returns an array of live paths because of which an
+ * object alive.
+ * describeRef -- returns description for a reference from a 'from'
+ * object to a 'to' object.
+ */
+function wrapHeapSnapshot(heap) {
+ function getClazz(clazz) {
+ if (clazz == undefined) clazz = "java.lang.Object";
+ var type = typeof(clazz);
+ if (type == "string") {
+ clazz = heap.findClass(clazz);
+ } else if (type == "object") {
+ clazz = unwrapJavaObject(clazz);
+ } else {
+ throw "class expected";;
+ }
+ return clazz;
+ }
+
+ // return heap as a script object with useful methods.
+ return {
+ snapshot: heap,
+
+ /**
+ * Class iteration: Calls callback function for each
+ * Java Class in the heap. Default callback function
+ * is 'print'. If callback returns true, the iteration
+ * is stopped.
+ *
+ * @param callback function to be called.
+ */
+ forEachClass: function(callback) {
+ if (callback == undefined) callback = print;
+ var classes = this.snapshot.classes;
+ while (classes.hasMoreElements()) {
+ if (callback(wrapJavaValue(classes.nextElement())))
+ return;
+ }
+ },
+
+ /**
+ * Returns an Enumeration of all roots.
+ */
+ roots: function() {
+ var e = this.snapshot.roots;
+ return new java.util.Enumeration() {
+ hasMoreElements: function() {
+ return e.hasMoreElements();
+ },
+ nextElement: function() {
+ return wrapRoot(e.nextElement());
+ }
+ };
+ },
+
+ /**
+ * Returns an Enumeration for all Java classes.
+ */
+ classes: function() {
+ return wrapIterator(this.snapshot.classes, true);
+ },
+
+ /**
+ * Object iteration: Calls callback function for each
+ * Java Object in the heap. Default callback function
+ * is 'print'.If callback returns true, the iteration
+ * is stopped.
+ *
+ * @param callback function to be called.
+ * @param clazz Class whose objects are retrieved.
+ * Optional, default is 'java.lang.Object'
+ * @param includeSubtypes flag to tell if objects of subtypes
+ * are included or not. optional, default is true.
+ */
+ forEachObject: function(callback, clazz, includeSubtypes) {
+ if (includeSubtypes == undefined) includeSubtypes = true;
+ if (callback == undefined) callback = print;
+ clazz = getClazz(clazz);
+
+ if (clazz) {
+ var instances = clazz.getInstances(includeSubtypes);
+ while (instances.hasNextElements()) {
+ if (callback(wrapJavaValue(instances.nextElement())))
+ return;
+ }
+ }
+ },
+
+ /**
+ * Returns an enumeration of Java objects in the heap.
+ *
+ * @param clazz Class whose objects are retrieved.
+ * Optional, default is 'java.lang.Object'
+ * @param includeSubtypes flag to tell if objects of subtypes
+ * are included or not. optional, default is true.
+ * @param where (optional) filter expression or function to
+ * filter the objects. The expression has to return true
+ * to include object passed to it in the result array.
+ * Built-in variable 'it' refers to the current object in
+ * filter expression.
+ */
+ objects: function(clazz, includeSubtypes, where) {
+ if (includeSubtypes == undefined) includeSubtypes = true;
+ if (where) {
+ if (typeof(where) == 'string') {
+ where = new Function("it", "return " + where);
+ }
+ }
+ clazz = getClazz(clazz);
+ if (clazz) {
+ var instances = clazz.getInstances(includeSubtypes);
+ if (where) {
+ return filterEnumeration(instances, where, true);
+ } else {
+ return wrapperEnumeration(instances);
+ }
+ } else {
+ return emptyEnumeration;
+ }
+ },
+
+ /**
+ * Find Java Class of given name.
+ *
+ * @param name class name
+ */
+ findClass: function(name) {
+ var clazz = this.snapshot.findClass(name + '');
+ return wrapJavaValue(clazz);
+ },
+
+ /**
+ * Find Java Object from given object id
+ *
+ * @param id object id as string
+ */
+ findObject: function(id) {
+ return wrapJavaValue(this.snapshot.findThing(id));
+ },
+
+ /**
+ * Returns an enumeration of objects in the finalizer
+ * queue waiting to be finalized.
+ */
+ finalizables: function() {
+ var tmp = this.snapshot.getFinalizerObjects();
+ return wrapperEnumeration(tmp);
+ },
+
+ /**
+ * Returns an array that contains objects referred from the
+ * given Java object directly or indirectly (i.e., all
+ * transitively referred objects are returned).
+ *
+ * @param jobject Java object whose reachables are returned.
+ */
+ reachables: function (jobject) {
+ return reachables(jobject, this.snapshot.reachableExcludes);
+ },
+
+ /**
+ * Returns array of paths of references by which the given
+ * Java object is live. Each path itself is an array of
+ * objects in the chain of references. Each path supports
+ * toHtml method that returns html description of the path.
+ *
+ * @param jobject Java object whose live paths are returned
+ * @param weak flag to indicate whether to include paths with
+ * weak references or not. default is false.
+ */
+ livepaths: function (jobject, weak) {
+ if (weak == undefined) {
+ weak = false;
+ }
+
+ function wrapRefChain(refChain) {
+ var path = new Array();
+
+ // compute path array from refChain
+ var tmp = refChain;
+ while (tmp != null) {
+ var obj = tmp.obj;
+ path[path.length] = wrapJavaValue(obj);
+ tmp = tmp.next;
+ }
+
+ function computeDescription(html) {
+ var root = refChain.obj.root;
+ var desc = root.description;
+ if (root.referer) {
+ var ref = root.referer;
+ desc += " (from " +
+ (html? toHtml(ref) : ref.toString()) + ')';
+ }
+ desc += '->';
+ var tmp = refChain;
+ while (tmp != null) {
+ var next = tmp.next;
+ var obj = tmp.obj;
+ desc += html? toHtml(obj) : obj.toString();
+ if (next != null) {
+ desc += " (" +
+ obj.describeReferenceTo(next.obj, heap) +
+ ") ->";
+ }
+ tmp = next;
+ }
+ return desc;
+ }
+
+ return new JSAdapter() {
+ __getIds__ : function() {
+ var res = new Array(path.length);
+ for (var i = 0; i < path.length; i++) {
+ res[i] = i;
+ }
+ return res;
+ },
+ __has__ : function (name) {
+ return (typeof(name) == 'number' &&
+ name >= 0 && name < path.length) ||
+ name == 'length' || name == 'toHtml' ||
+ name == 'toString';
+ },
+ __get__ : function(name) {
+ if (typeof(name) == 'number' &&
+ name >= 0 && name < path.length) {
+ return path[name];
+ } else if (name == 'length') {
+ return path.length;
+ } else if (name == 'toHtml') {
+ return function() {
+ return computeDescription(true);
+ }
+ } else if (name == 'toString') {
+ return function() {
+ return computeDescription(false);
+ }
+ } else {
+ return undefined;
+ }
+ },
+ };
+ }
+
+ jobject = unwrapJavaObject(jobject);
+ var refChains = this.snapshot.rootsetReferencesTo(jobject, weak);
+ var paths = new Array(refChains.length);
+ for (var i in refChains) {
+ paths[i] = wrapRefChain(refChains[i]);
+ }
+ return paths;
+ },
+
+ /**
+ * Return description string for reference from 'from' object
+ * to 'to' Java object.
+ *
+ * @param from source Java object
+ * @param to destination Java object
+ */
+ describeRef: function (from, to) {
+ from = unwrapJavaObject(from);
+ to = unwrapJavaObject(to);
+ return from.describeReferenceTo(to, this.snapshot);
+ },
+ };
+}
+
+// per-object functions
+
+/**
+ * Returns allocation site trace (if available) of a Java object
+ *
+ * @param jobject object whose allocation site trace is returned
+ */
+function allocTrace(jobject) {
+ try {
+ jobject = unwrapJavaObject(jobject);
+ var trace = jobject.allocatedFrom;
+ return (trace != null) ? trace.frames : null;
+ } catch (e) {
+ print("allocTrace: " + jobject + ", " + e);
+ return null;
+ }
+}
+
+/**
+ * Returns Class object for given Java object
+ *
+ * @param jobject object whose Class object is returned
+ */
+function classof(jobject) {
+ jobject = unwrapJavaObject(jobject);
+ return wrapJavaValue(jobject.clazz);
+}
+
+/**
+ * Find referers (a.k.a in-coming references). Calls callback
+ * for each referrer of the given Java object. If the callback
+ * returns true, the iteration is stopped.
+ *
+ * @param callback function to call for each referer
+ * @param jobject object whose referers are retrieved
+ */
+function forEachReferrer(callback, jobject) {
+ jobject = unwrapJavaObject(jobject);
+ var refs = jobject.referers;
+ while (refs.hasMoreElements()) {
+ if (callback(wrapJavaValue(refs.nextElement()))) {
+ return;
+ }
+ }
+}
+
+/**
+ * Compares two Java objects for object identity.
+ *
+ * @param o1, o2 objects to compare for identity
+ */
+function identical(o1, o2) {
+ return objectid(o1) == objectid(o2);
+}
+
+/**
+ * Returns Java object id as string
+ *
+ * @param jobject object whose id is returned
+ */
+function objectid(jobject) {
+ try {
+ jobject = unwrapJavaObject(jobject);
+ return String(jobject.idString);
+ } catch (e) {
+ print("objectid: " + jobject + ", " + e);
+ return null;
+ }
+}
+
+/**
+ * Prints allocation site trace of given object
+ *
+ * @param jobject object whose allocation site trace is returned
+ */
+function printAllocTrace(jobject) {
+ var frames = this.allocTrace(jobject);
+ if (frames == null || frames.length == 0) {
+ print("allocation site trace unavailable for " +
+ objectid(jobject));
+ return;
+ }
+ print(objectid(jobject) + " was allocated at ..");
+ for (var i in frames) {
+ var frame = frames[i];
+ var src = frame.sourceFileName;
+ if (src == null) src = '<unknown source>';
+ print('\t' + frame.className + "." +
+ frame.methodName + '(' + frame.methodSignature + ') [' +
+ src + ':' + frame.lineNumber + ']');
+ }
+}
+
+/**
+ * Returns an enumeration of referrers of the given Java object.
+ *
+ * @param jobject Java object whose referrers are returned.
+ */
+function referrers(jobject) {
+ try {
+ jobject = unwrapJavaObject(jobject);
+ return wrapperEnumeration(jobject.referers);
+ } catch (e) {
+ print("referrers: " + jobject + ", " + e);
+ return emptyEnumeration;
+ }
+}
+
+/**
+ * Returns an array that contains objects referred from the
+ * given Java object.
+ *
+ * @param jobject Java object whose referees are returned.
+ */
+function referees(jobject) {
+ var res = new Array();
+ jobject = unwrapJavaObject(jobject);
+ if (jobject != undefined) {
+ try {
+ jobject.visitReferencedObjects(
+ new hatPkg.model.JavaHeapObjectVisitor() {
+ visit: function(other) {
+ res[res.length] = wrapJavaValue(other);
+ },
+ exclude: function(clazz, field) {
+ return false;
+ },
+ mightExclude: function() {
+ return false;
+ }
+ });
+ } catch (e) {
+ print("referees: " + jobject + ", " + e);
+ }
+ }
+ return res;
+}
+
+/**
+ * Returns an array that contains objects referred from the
+ * given Java object directly or indirectly (i.e., all
+ * transitively referred objects are returned).
+ *
+ * @param jobject Java object whose reachables are returned.
+ * @param excludes optional comma separated list of fields to be
+ * removed in reachables computation. Fields are
+ * written as class_name.field_name form.
+ */
+function reachables(jobject, excludes) {
+ if (excludes == undefined) {
+ excludes = null;
+ } else if (typeof(excludes) == 'string') {
+ var st = new java.util.StringTokenizer(excludes, ",");
+ var excludedFields = new Array();
+ while (st.hasMoreTokens()) {
+ excludedFields[excludedFields.length] = st.nextToken().trim();
+ }
+ if (excludedFields.length > 0) {
+ excludes = new hatPkg.model.ReachableExcludes() {
+ isExcluded: function (field) {
+ for (var index in excludedFields) {
+ if (field.equals(excludedFields[index])) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ } else {
+ // nothing to filter...
+ excludes = null;
+ }
+ } else if (! (excludes instanceof hatPkg.model.ReachableExcludes)) {
+ excludes = null;
+ }
+
+ jobject = unwrapJavaObject(jobject);
+ var ro = new hatPkg.model.ReachableObjects(jobject, excludes);
+ var tmp = ro.reachables;
+ var res = new Array(tmp.length);
+ for (var i in tmp) {
+ res[i] = wrapJavaValue(tmp[i]);
+ }
+ return res;
+}
+
+
+/**
+ * Returns whether 'from' object refers to 'to' object or not.
+ *
+ * @param from Java object that is source of the reference.
+ * @param to Java object that is destination of the reference.
+ */
+function refers(from, to) {
+ try {
+ var tmp = unwrapJavaObject(from);
+ if (tmp instanceof hatPkg.model.JavaClass) {
+ from = from.statics;
+ } else if (tmp instanceof hatPkg.model.JavaValueArray) {
+ return false;
+ }
+ for (var i in from) {
+ if (identical(from[i], to)) {
+ return true;
+ }
+ }
+ } catch (e) {
+ print("refers: " + from + ", " + e);
+ }
+ return false;
+}
+
+/**
+ * If rootset includes given jobject, return Root
+ * object explanining the reason why it is a root.
+ *
+ * @param jobject object whose Root is returned
+ */
+function root(jobject) {
+ try {
+ jobject = unwrapJavaObject(jobject);
+ return wrapRoot(jobject.root);
+ } catch (e) {
+ return null;
+ }
+}
+
+/**
+ * Returns size of the given Java object
+ *
+ * @param jobject object whose size is returned
+ */
+function sizeof(jobject) {
+ try {
+ jobject = unwrapJavaObject(jobject);
+ return jobject.size;
+ } catch (e) {
+ print("sizeof: " + jobject + ", " + e);
+ return null;
+ }
+}
+
+/**
+ * Returns String by replacing Unicode chars and
+ * HTML special chars (such as '<') with entities.
+ *
+ * @param str string to be encoded
+ */
+function encodeHtml(str) {
+ return hatPkg.util.Misc.encodeHtml(str);
+}
+
+/**
+ * Returns HTML string for the given object.
+ *
+ * @param obj object for which HTML string is returned.
+ */
+function toHtml(obj) {
+ if (obj == null) {
+ return "null";
+ }
+
+ if (obj == undefined) {
+ return "undefined";
+ }
+
+ var tmp = unwrapJavaObject(obj);
+ if (tmp != undefined) {
+ var id = tmp.idString;
+ if (tmp instanceof Packages.com.sun.tools.hat.internal.model.JavaClass) {
+ var name = tmp.name;
+ return "<a href='/class/" + id + "'>class " + name + "</a>";
+ } else {
+ var name = tmp.clazz.name;
+ return "<a href='/object/" + id + "'>" +
+ name + "@" + id + "</a>";
+ }
+ } else if ((typeof(obj) == 'object') || (obj instanceof JSAdapter)) {
+ if (obj instanceof java.lang.Object) {
+ // script wrapped Java object
+ obj = wrapIterator(obj);
+ // special case for enumeration
+ if (obj instanceof java.util.Enumeration) {
+ var res = "[ ";
+ while (obj.hasMoreElements()) {
+ res += toHtml(obj.nextElement()) + ", ";
+ }
+ res += "]";
+ return res;
+ } else {
+ return obj;
+ }
+ } else if (obj instanceof Array) {
+ // script array
+ var res = "[ ";
+ for (var i in obj) {
+ res += toHtml(obj[i]);
+ if (i != obj.length - 1) {
+ res += ", ";
+ }
+ }
+ res += " ]";
+ return res;
+ } else {
+ // if the object has a toHtml function property
+ // just use that...
+ if (typeof(obj.toHtml) == 'function') {
+ return obj.toHtml();
+ } else {
+ // script object
+ var res = "{ ";
+ for (var i in obj) {
+ res += i + ":" + toHtml(obj[i]) + ", ";
+ }
+ res += "}";
+ return res;
+ }
+ }
+ } else {
+ // JavaScript primitive value
+ return obj;
+ }
+}
+
+/*
+ * Generic array/iterator/enumeration [or even object!] manipulation
+ * functions. These functions accept an array/iteration/enumeration
+ * and expression String or function. These functions iterate each
+ * element of array and apply the expression/function on each element.
+ */
+
+// private function to wrap an Iterator as an Enumeration
+function wrapIterator(itr, wrap) {
+ if (itr instanceof java.util.Iterator) {
+ return new java.util.Enumeration() {
+ hasMoreElements: function() {
+ return itr.hasNext();
+ },
+ nextElement: function() {
+ return wrap? wrapJavaValue(itr.next()) : itr.next();
+ }
+ };
+ } else {
+ return itr;
+ }
+}
+
+/**
+ * Converts an enumeration/iterator/object into an array
+ *
+ * @param obj enumeration/iterator/object
+ * @return array that contains values of enumeration/iterator/object
+ */
+function toArray(obj) {
+ obj = wrapIterator(obj);
+ if (obj instanceof java.util.Enumeration) {
+ var res = new Array();
+ while (obj.hasMoreElements()) {
+ res[res.length] = obj.nextElement();
+ }
+ return res;
+ } else if (obj instanceof Array) {
+ return obj;
+ } else {
+ var res = new Array();
+ for (var index in obj) {
+ res[res.length] = obj[index];
+ }
+ return res;
+ }
+}
+
+/**
+ * Returns whether the given array/iterator/enumeration contains
+ * an element that satisfies the given boolean expression specified
+ * in code.
+ *
+ * @param array input array/iterator/enumeration that is iterated
+ * @param code expression string or function
+ * @return boolean result
+ *
+ * The code evaluated can refer to the following built-in variables.
+ *
+ * 'it' -> currently visited element
+ * 'index' -> index of the current element
+ * 'array' -> array that is being iterated
+ */
+function contains(array, code) {
+ array = wrapIterator(array);
+ var func = code;
+ if (typeof(func) != 'function') {
+ func = new Function("it", "index", "array", "return " + code);
+ }
+
+ if (array instanceof java.util.Enumeration) {
+ var index = 0;
+ while (array.hasMoreElements()) {
+ var it = array.nextElement();
+ if (func(it, index, array)) {
+ return true;
+ }
+ index++;
+ }
+ } else {
+ for (var index in array) {
+ var it = array[index];
+ if (func(it, index, array)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * concatenates two arrays/iterators/enumerators.
+ *
+ * @param array1 array/iterator/enumeration
+ * @param array2 array/iterator/enumeration
+ *
+ * @return concatenated array or composite enumeration
+ */
+function concat(array1, array2) {
+ array1 = wrapIterator(array1);
+ array2 = wrapIterator(array2);
+ if (array1 instanceof Array && array2 instanceof Array) {
+ return array1.concat(array2);
+ } else if (array1 instanceof java.util.Enumeration &&
+ array2 instanceof java.util.Enumeration) {
+ return new Packages.com.sun.tools.hat.internal.util.CompositeEnumeration(array1, array2);
+ } else {
+ return undefined;
+ }
+}
+
+/**
+ * Returns the number of array/iterator/enumeration elements
+ * that satisfy the given boolean expression specified in code.
+ * The code evaluated can refer to the following built-in variables.
+ *
+ * @param array input array/iterator/enumeration that is iterated
+ * @param code expression string or function
+ * @return number of elements
+ *
+ * 'it' -> currently visited element
+ * 'index' -> index of the current element
+ * 'array' -> array that is being iterated
+ */
+function count(array, code) {
+ if (code == undefined) {
+ return length(array);
+ }
+ array = wrapIterator(array);
+ var func = code;
+ if (typeof(func) != 'function') {
+ func = new Function("it", "index", "array", "return " + code);
+ }
+
+ var result = 0;
+ if (array instanceof java.util.Enumeration) {
+ var index = 0;
+ while (array.hasMoreElements()) {
+ var it = array.nextElement();
+ if (func(it, index, array)) {
+ result++;
+ }
+ index++;
+ }
+ } else {
+ for (var index in array) {
+ var it = array[index];
+ if (func(it, index, array)) {
+ result++;
+ }
+ }
+ }
+ return result;
+}
+
+/**
+ * filter function returns an array/enumeration that contains
+ * elements of the input array/iterator/enumeration that satisfy
+ * the given boolean expression. The boolean expression code can
+ * refer to the following built-in variables.
+ *
+ * @param array input array/iterator/enumeration that is iterated
+ * @param code expression string or function
+ * @return array/enumeration that contains the filtered elements
+ *
+ * 'it' -> currently visited element
+ * 'index' -> index of the current element
+ * 'array' -> array that is being iterated
+ * 'result' -> result array
+ */
+function filter(array, code) {
+ array = wrapIterator(array);
+ var func = code;
+ if (typeof(code) != 'function') {
+ func = new Function("it", "index", "array", "result", "return " + code);
+ }
+ if (array instanceof java.util.Enumeration) {
+ return filterEnumeration(array, func, false);
+ } else {
+ var result = new Array();
+ for (var index in array) {
+ var it = array[index];
+ if (func(it, index, array, result)) {
+ result[result.length] = it;
+ }
+ }
+ return result;
+ }
+}
+
+/**
+ * Returns the number of elements of array/iterator/enumeration.
+ *
+ * @param array input array/iterator/enumeration that is iterated
+ */
+function length(array) {
+ array = wrapIterator(array);
+ if (array instanceof Array) {
+ return array.length;
+ } else if (array instanceof java.util.Enumeration) {
+ var cnt = 0;
+ while (array.hasMoreElements()) {
+ array.nextElement();
+ cnt++;
+ }
+ return cnt;
+ } else {
+ var cnt = 0;
+ for (var index in array) {
+ cnt++;
+ }
+ return cnt;
+ }
+}
+
+/**
+ * Transforms the given object or array by evaluating given code
+ * on each element of the object or array. The code evaluated
+ * can refer to the following built-in variables.
+ *
+ * @param array input array/iterator/enumeration that is iterated
+ * @param code expression string or function
+ * @return array/enumeration that contains mapped values
+ *
+ * 'it' -> currently visited element
+ * 'index' -> index of the current element
+ * 'array' -> array that is being iterated
+ * 'result' -> result array
+ *
+ * map function returns an array/enumeration of values created
+ * by repeatedly calling code on each element of the input
+ * array/iterator/enumeration.
+ */
+function map(array, code) {
+ array = wrapIterator(array);
+ var func = code;
+ if(typeof(code) != 'function') {
+ func = new Function("it", "index", "array", "result", "return " + code);
+ }
+
+ if (array instanceof java.util.Enumeration) {
+ var index = 0;
+ var result = new java.util.Enumeration() {
+ hasMoreElements: function() {
+ return array.hasMoreElements();
+ },
+ nextElement: function() {
+ return func(array.nextElement(), index++, array, result);
+ }
+ };
+ return result;
+ } else {
+ var result = new Array();
+ for (var index in array) {
+ var it = array[index];
+ result[result.length] = func(it, index, array, result);
+ }
+ return result;
+ }
+}
+
+// private function used by min, max functions
+function minmax(array, code) {
+ if (typeof(code) == 'string') {
+ code = new Function("lhs", "rhs", "return " + code);
+ }
+ array = wrapIterator(array);
+ if (array instanceof java.util.Enumeration) {
+ if (! array.hasMoreElements()) {
+ return undefined;
+ }
+ var res = array.nextElement();
+ while (array.hasMoreElements()) {
+ var next = array.nextElement();
+ if (code(next, res)) {
+ res = next;
+ }
+ }
+ return res;
+ } else {
+ if (array.length == 0) {
+ return undefined;
+ }
+ var res = array[0];
+ for (var index = 1; index < array.length; index++) {
+ if (code(array[index], res)) {
+ res = array[index];
+ }
+ }
+ return res;
+ }
+}
+
+/**
+ * Returns the maximum element of the array/iterator/enumeration
+ *
+ * @param array input array/iterator/enumeration that is iterated
+ * @param code (optional) comparision expression or function
+ * by default numerical maximum is computed.
+ */
+function max(array, code) {
+ if (code == undefined) {
+ code = function (lhs, rhs) { return lhs > rhs; }
+ }
+ return minmax(array, code);
+}
+
+/**
+ * Returns the minimum element of the array/iterator/enumeration
+ *
+ * @param array input array/iterator/enumeration that is iterated
+ * @param code (optional) comparision expression or function
+ * by default numerical minimum is computed.
+ */
+function min(array, code) {
+ if (code == undefined) {
+ code = function (lhs, rhs) { return lhs < rhs; }
+ }
+ return minmax(array, code);
+}
+
+/**
+ * sort function sorts the input array. optionally accepts
+ * code to compare the elements. If code is not supplied,
+ * numerical sort is done.
+ *
+ * @param array input array/iterator/enumeration that is sorted
+ * @param code expression string or function
+ * @return sorted array
+ *
+ * The comparison expression can refer to the following
+ * built-in variables:
+ *
+ * 'lhs' -> 'left side' element
+ * 'rhs' -> 'right side' element
+ */
+function sort(array, code) {
+ // we need an array to sort, so convert non-arrays
+ array = toArray(array);
+
+ // by default use numerical comparison
+ var func = code;
+ if (code == undefined) {
+ func = function(lhs, rhs) { return lhs - rhs; };
+ } else if (typeof(code) == 'string') {
+ func = new Function("lhs", "rhs", "return " + code);
+ }
+ return array.sort(func);
+}
+
+/**
+ * Returns the sum of the elements of the array
+ *
+ * @param array input array that is summed.
+ * @param code optional expression used to map
+ * input elements before sum.
+ */
+function sum(array, code) {
+ array = wrapIterator(array);
+ if (code != undefined) {
+ array = map(array, code);
+ }
+ var result = 0;
+ if (array instanceof java.util.Enumeration) {
+ while (array.hasMoreElements()) {
+ result += Number(array.nextElement());
+ }
+ } else {
+ for (var index in array) {
+ result += Number(array[index]);
+ }
+ }
+ return result;
+}
+
+/**
+ * Returns array of unique elements from the given input
+ * array/iterator/enumeration.
+ *
+ * @param array from which unique elements are returned.
+ * @param code optional expression (or function) giving unique
+ * attribute/property for each element.
+ * by default, objectid is used for uniqueness.
+ */
+function unique(array, code) {
+ array = wrapIterator(array);
+ if (code == undefined) {
+ code = new Function("it", "return objectid(it);");
+ } else if (typeof(code) == 'string') {
+ code = new Function("it", "return " + code);
+ }
+ var tmp = new Object();
+ if (array instanceof java.util.Enumeration) {
+ while (array.hasMoreElements()) {
+ var it = array.nextElement();
+ tmp[code(it)] = it;
+ }
+ } else {
+ for (var index in array) {
+ var it = array[index];
+ tmp[code(it)] = it;
+ }
+ }
+ var res = new Array();
+ for (var index in tmp) {
+ res[res.length] = tmp[index];
+ }
+ return res;
+}