jdk/src/jdk.dev/share/classes/com/sun/tools/hat/resources/hat.js
changeset 30779 92bb39a2a876
parent 30778 43087a23d825
parent 30736 ff3fc75f3214
child 30780 b83f001a855d
child 30886 d2a0ec86d6ef
equal deleted inserted replaced
30778:43087a23d825 30779:92bb39a2a876
     1 /*
       
     2  * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 /*
       
    27  * The Original Code is HAT. The Initial Developer of the
       
    28  * Original Code is Bill Foote, with contributions from others
       
    29  * at JavaSoft/Sun.
       
    30  */
       
    31 
       
    32 var hatPkg = Packages.com.sun.tools.hat.internal;
       
    33 
       
    34 /**
       
    35  * This is JavaScript interface for heap analysis using HAT
       
    36  * (Heap Analysis Tool). HAT classes are referred from
       
    37  * this file. In particular, refer to classes in hat.model 
       
    38  * package.
       
    39  * 
       
    40  * HAT model objects are wrapped as convenient script objects so that
       
    41  * fields may be accessed in natural syntax. For eg. Java fields can be
       
    42  * accessed with obj.field_name syntax and array elements can be accessed
       
    43  * with array[index] syntax. 
       
    44  */
       
    45 
       
    46 // returns an enumeration that wraps elements of
       
    47 // the input enumeration elements.
       
    48 function wrapperEnumeration(e) {
       
    49     return new java.util.Enumeration() {
       
    50         hasMoreElements: function() {
       
    51             return e.hasMoreElements();
       
    52         },
       
    53         nextElement: function() {
       
    54             return wrapJavaValue(e.nextElement());
       
    55         }
       
    56     };
       
    57 }
       
    58 
       
    59 // returns an enumeration that filters out elements
       
    60 // of input enumeration using the filter function.
       
    61 function filterEnumeration(e, func, wrap) {
       
    62     var next = undefined;
       
    63     var index = 0;
       
    64 
       
    65     function findNext() {
       
    66         var tmp;
       
    67         while (e.hasMoreElements()) {
       
    68             tmp = e.nextElement();
       
    69             index++;
       
    70             if (wrap) {
       
    71                 tmp = wrapJavaValue(tmp);
       
    72             }
       
    73             if (func(tmp, index, e)) {
       
    74                 next = tmp;
       
    75                 return;
       
    76             }
       
    77         }
       
    78     }
       
    79 
       
    80     return new java.util.Enumeration() {
       
    81         hasMoreElements: function() {
       
    82             findNext();
       
    83             return next != undefined;
       
    84         },
       
    85 
       
    86         nextElement: function() {
       
    87             if (next == undefined) {
       
    88                 // user may not have called hasMoreElements?
       
    89                 findNext();
       
    90             }
       
    91             if (next == undefined) {
       
    92                 throw "NoSuchElementException";
       
    93             }
       
    94             var res = next;
       
    95             next = undefined;
       
    96             return res;
       
    97         }
       
    98     };
       
    99 }
       
   100 
       
   101 // enumeration that has no elements ..
       
   102 var emptyEnumeration = new java.util.Enumeration() {
       
   103         hasMoreElements: function() { 
       
   104             return false;
       
   105         },
       
   106         nextElement: function() {
       
   107             throw "NoSuchElementException";
       
   108         }
       
   109     };
       
   110 
       
   111 function wrapRoot(root) {
       
   112     if (root) {
       
   113         return {
       
   114             id: root.idString,
       
   115             description: root.description,
       
   116             referrer: wrapJavaValue(root.referer),
       
   117             type: root.typeName
       
   118         };
       
   119     } else {
       
   120         return null;
       
   121     }
       
   122 }
       
   123 
       
   124 function JavaClassProto() {    
       
   125     function jclass(obj) {
       
   126         return obj['wrapped-object'];
       
   127     }
       
   128 
       
   129     // return whether given class is subclass of this class or not
       
   130     this.isSubclassOf = function(other) {
       
   131         var tmp = jclass(this);
       
   132         var otherid = objectid(other);
       
   133         while (tmp != null) {
       
   134             if (otherid.equals(tmp.idString)) {
       
   135                 return true;
       
   136             }
       
   137             tmp = tmp.superclass;
       
   138         }
       
   139         return false;
       
   140     }
       
   141 
       
   142     // return whether given class is superclass of this class or not
       
   143     this.isSuperclassOf = function(other) {
       
   144         return other.isSubclassOf(this); 
       
   145     }
       
   146 
       
   147     // includes direct and indirect superclasses
       
   148     this.superclasses = function() {
       
   149         var res = new Array();
       
   150         var tmp = this.superclass;
       
   151         while (tmp != null) {
       
   152             res[res.length] = tmp;
       
   153             tmp = tmp.superclass;
       
   154         }
       
   155         return res;
       
   156     }
       
   157 
       
   158     /**
       
   159      * Returns an array containing subclasses of this class.
       
   160      *
       
   161      * @param indirect should include indirect subclasses or not.
       
   162      *                 default is true.
       
   163      */
       
   164     this.subclasses = function(indirect) {
       
   165         if (indirect == undefined) indirect = true;
       
   166         var classes = jclass(this).subclasses;
       
   167         var res = new Array();
       
   168         for (var i in classes) {
       
   169             var subclass = wrapJavaValue(classes[i]);
       
   170             res[res.length] = subclass;
       
   171             if (indirect) {
       
   172                 res = res.concat(subclass.subclasses());
       
   173             }
       
   174         }
       
   175         return res;
       
   176     }
       
   177     this.toString = function() { return jclass(this).toString(); }
       
   178 }
       
   179 
       
   180 var theJavaClassProto = new JavaClassProto();
       
   181 
       
   182 // Script wrapper for HAT model objects, values.
       
   183 // wraps a Java value as appropriate for script object
       
   184 function wrapJavaValue(thing) {
       
   185     if (thing == null || thing == undefined ||
       
   186         thing instanceof hatPkg.model.HackJavaValue) {
       
   187 	return null;
       
   188     } 
       
   189     
       
   190     if (thing instanceof hatPkg.model.JavaValue) {
       
   191         // map primitive values to closest JavaScript primitives
       
   192         if (thing instanceof hatPkg.model.JavaBoolean) {
       
   193             return thing.toString() == "true";
       
   194         } else if (thing instanceof hatPkg.model.JavaChar) {
       
   195             return thing.toString() + '';
       
   196         } else {
       
   197             return java.lang.Double.parseDouble(thing.toString());
       
   198         }			
       
   199     } else {
       
   200         // wrap Java object as script object
       
   201         return wrapJavaObject(thing);		
       
   202     }
       
   203 }
       
   204 
       
   205 // wrap Java object with appropriate script object
       
   206 function wrapJavaObject(thing) {
       
   207 
       
   208     // HAT Java model object wrapper. Handles all cases 
       
   209     // (instance, object/primitive array and Class objects)	
       
   210     function javaObject(jobject) {		
       
   211         // FIXME: Do I need this? or can I assume that these would
       
   212         // have been resolved already?
       
   213         if (jobject instanceof hatPkg.model.JavaObjectRef) {
       
   214             jobject = jobject.dereference();
       
   215             if (jobject instanceof hatPkg.model.HackJavaValue) {
       
   216                 print(jobject);
       
   217                 return null;
       
   218             }
       
   219         }
       
   220 
       
   221         if (jobject instanceof hatPkg.model.JavaObject) {
       
   222             return new JavaObjectWrapper(jobject);
       
   223         } else if (jobject instanceof hatPkg.model.JavaClass) {
       
   224             return new JavaClassWrapper(jobject);
       
   225         } else if (jobject instanceof hatPkg.model.JavaObjectArray) {
       
   226             return new JavaObjectArrayWrapper(jobject);
       
   227         } else if (jobject instanceof hatPkg.model.JavaValueArray) {
       
   228             return new JavaValueArrayWrapper(jobject);
       
   229         } else {
       
   230             print("unknown heap object type: " + jobject.getClass());
       
   231             return jobject;
       
   232         }
       
   233     }
       
   234     
       
   235     // returns wrapper for Java instances
       
   236     function JavaObjectWrapper(instance) {
       
   237         var things = instance.fields;
       
   238         var fields = instance.clazz.fieldsForInstance;
       
   239     		
       
   240         // instance fields can be accessed in natural syntax
       
   241         return new JSAdapter() {
       
   242             __getIds__ : function() {
       
   243                     var res = new Array(fields.length);
       
   244                     for (var i in fields) {
       
   245                         res[i] = fields[i].name;
       
   246                     }
       
   247                     return res;
       
   248             },
       
   249             __has__ : function(name) {
       
   250                     for (var i in fields) {
       
   251                         if (name == fields[i].name) return true;
       
   252                     }
       
   253                     return name == 'class' || name == 'toString' ||
       
   254                            name == 'wrapped-object';
       
   255             },
       
   256             __get__ : function(name) {
       
   257     
       
   258                     for (var i in fields) {
       
   259                         if(fields[i].name == name) {
       
   260                             return wrapJavaValue(things[i]);
       
   261                         }
       
   262                     }
       
   263     
       
   264                     if (name == 'class') {
       
   265                         return wrapJavaValue(instance.clazz);
       
   266                     } else if (name == 'wrapped-object') {
       
   267                         return instance;
       
   268                     } 
       
   269     
       
   270                     return undefined;
       
   271             },
       
   272             __call__: function(name) {
       
   273                 if (name == 'toString') {
       
   274                     return instance.toString();
       
   275                 } else {
       
   276                     return undefined;
       
   277                 }
       
   278             } 
       
   279         }				
       
   280     }
       
   281 
       
   282 
       
   283     // return wrapper for Java Class objects
       
   284     function JavaClassWrapper(jclass) {	
       
   285         var fields = jclass.statics;
       
   286     
       
   287         // to access static fields of given Class cl, use 
       
   288         // cl.statics.<static-field-name> syntax
       
   289         this.statics = new JSAdapter() {
       
   290             __getIds__ : function() {
       
   291                 var res = new Array(fields.length);
       
   292                 for (var i in fields) {
       
   293                     res[i] = fields[i].field.name;
       
   294                 }
       
   295                 return res;
       
   296             },
       
   297             __has__ : function(name) {
       
   298                 for (var i in fields) {
       
   299                     if (name == fields[i].field.name) {
       
   300                         return true;
       
   301                     }					
       
   302                 }
       
   303                 return false;
       
   304             },
       
   305             __get__ : function(name) {
       
   306                 for (var i in fields) {
       
   307                     if (name == fields[i].field.name) {
       
   308                         return wrapJavaValue(fields[i].value);	
       
   309                     }					
       
   310                 }
       
   311                 return undefined;
       
   312             }
       
   313         }
       
   314     		
       
   315         if (jclass.superclass != null) {
       
   316             this.superclass = wrapJavaValue(jclass.superclass);
       
   317         } else {
       
   318             this.superclass = null;
       
   319         }
       
   320 
       
   321         this.loader = wrapJavaValue(jclass.getLoader());
       
   322         this.signers = wrapJavaValue(jclass.getSigners());
       
   323         this.protectionDomain = wrapJavaValue(jclass.getProtectionDomain());
       
   324         this.instanceSize = jclass.instanceSize;
       
   325         this.name = jclass.name; 
       
   326         this.fields = jclass.fields;
       
   327         this['wrapped-object'] = jclass;
       
   328     }
       
   329 
       
   330     for (var i in theJavaClassProto) {
       
   331         if (typeof theJavaClassProto[i] == 'function') {
       
   332            JavaClassWrapper.prototype[i] = theJavaClassProto[i];
       
   333         }
       
   334     }
       
   335     
       
   336     // returns wrapper for Java object arrays
       
   337     function JavaObjectArrayWrapper(array) {
       
   338         var elements = array.elements;
       
   339         // array elements can be accessed in natural syntax
       
   340         // also, 'length' property is supported.
       
   341         return new JSAdapter() {
       
   342             __getIds__ : function() {
       
   343                 var res = new Array(elements.length);
       
   344                 for (var i = 0; i < elements.length; i++) {
       
   345                     res[i] = String(i);
       
   346                 }
       
   347                 return res;
       
   348             },
       
   349             __has__: function(name) {
       
   350                 return (name >= 0 && name < elements.length)  ||
       
   351                         name == 'length' || name == 'class' ||
       
   352                         name == 'toString' || name == 'wrapped-object';
       
   353             },
       
   354             __get__ : function(name) {
       
   355                 if (name >= 0 && name < elements.length) {
       
   356                     return wrapJavaValue(elements[name]);
       
   357                 } else if (name == 'length') {
       
   358                     return elements.length;
       
   359                 } else if (name == 'class') {
       
   360                     return wrapJavaValue(array.clazz);
       
   361                 } else if (name == 'wrapped-object') {
       
   362                     return array;
       
   363                 } else {
       
   364                     return undefined;
       
   365                 }				
       
   366             },
       
   367             __call__: function(name) {
       
   368                 if (name == 'toString') {
       
   369                     return array.toString();
       
   370                 } else {
       
   371                     return undefined;
       
   372                 }
       
   373             } 
       
   374         }			
       
   375     }
       
   376     
       
   377     // returns wrapper for Java primitive arrays
       
   378     function JavaValueArrayWrapper(array) {
       
   379         var type = String(java.lang.Character.toString(array.elementType));
       
   380         var elements = array.elements;
       
   381         // array elements can be accessed in natural syntax
       
   382         // also, 'length' property is supported.
       
   383         return new JSAdapter() {
       
   384             __getIds__ : function() {
       
   385                 var r = new Array(array.length);
       
   386                 for (var i = 0; i < array.length; i++) {
       
   387                     r[i] = String(i);
       
   388                 }
       
   389                 return r;
       
   390             },
       
   391             __has__: function(name) {
       
   392                 return (name >= 0 && name < array.length) ||
       
   393                         name == 'length' || name == 'class' ||
       
   394                         name == 'toString' || name == 'wrapped-object';
       
   395             },
       
   396             __get__: function(name) {
       
   397                 if (name >= 0 && name < array.length) {
       
   398                     return elements[name];
       
   399                 }
       
   400     
       
   401                 if (name == 'length') {
       
   402                     return array.length;
       
   403                 } else if (name == 'wrapped-object') {
       
   404                     return array;
       
   405                 } else if (name == 'class') {
       
   406                     return wrapJavaValue(array.clazz);
       
   407                 } else {
       
   408                     return undefined;
       
   409                 }
       
   410             },
       
   411             __call__: function(name) {
       
   412                 if (name == 'toString') {
       
   413                     return array.valueString(true);
       
   414                 } else {
       
   415                     return undefined;
       
   416                 }
       
   417             } 
       
   418         }
       
   419     }
       
   420     return javaObject(thing);
       
   421 }
       
   422 
       
   423 // unwrap a script object to corresponding HAT object
       
   424 function unwrapJavaObject(jobject) {
       
   425     if (!(jobject instanceof hatPkg.model.JavaHeapObject)) {
       
   426         try {
       
   427             jobject = jobject["wrapped-object"];
       
   428         } catch (e) {
       
   429             print("unwrapJavaObject: " + jobject + ", " + e);
       
   430             jobject = undefined;
       
   431         }
       
   432     }
       
   433     return jobject;
       
   434 }
       
   435 
       
   436 /**
       
   437  * readHeapDump parses a heap dump file and returns script wrapper object.
       
   438  *
       
   439  * @param file  Heap dump file name
       
   440  * @param stack flag to tell if allocation site traces are available
       
   441  * @param refs  flag to tell if backward references are needed or not
       
   442  * @param debug debug level for HAT
       
   443  * @return heap as a JavaScript object
       
   444  */
       
   445 function readHeapDump(file, stack, refs, debug) {
       
   446 
       
   447     // default value of debug is 0
       
   448     if (!debug) debug = 0;
       
   449 
       
   450     // by default, we assume no stack traces
       
   451     if (!stack) stack = false;
       
   452 
       
   453     // by default, backward references are resolved
       
   454     if (!refs) refs = true;
       
   455 
       
   456     // read the heap dump 
       
   457     var heap = hatPkg.parser.HprofReader.readFile(file, stack, debug);
       
   458 
       
   459     // resolve it
       
   460     heap.resolve(refs);
       
   461 
       
   462     // wrap Snapshot as convenient script object
       
   463     return wrapHeapSnapshot(heap);
       
   464 }
       
   465 
       
   466 /**
       
   467  * The result object supports the following methods:
       
   468  * 
       
   469  *  forEachClass  -- calls a callback for each Java Class
       
   470  *  forEachObject -- calls a callback for each Java object
       
   471  *  findClass -- finds Java Class of given name
       
   472  *  findObject -- finds object from given object id
       
   473  *  objects -- returns all objects of given class as an enumeration
       
   474  *  classes -- returns all classes in the heap as an enumeration
       
   475  *  reachables -- returns all objects reachable from a given object
       
   476  *  livepaths -- returns an array of live paths because of which an
       
   477  *               object alive.
       
   478  *  describeRef -- returns description for a reference from a 'from' 
       
   479  *              object to a 'to' object.
       
   480  */
       
   481 function wrapHeapSnapshot(heap) {
       
   482     function getClazz(clazz) {
       
   483         if (clazz == undefined) clazz = "java.lang.Object";
       
   484         var type = typeof(clazz);
       
   485         if (type == "string") {
       
   486             clazz = heap.findClass(clazz);		
       
   487         } else if (type == "object") {
       
   488             clazz = unwrapJavaObject(clazz);
       
   489         } else {
       
   490             throw "class expected";;
       
   491         }
       
   492         return clazz;
       
   493     }
       
   494 
       
   495     // return heap as a script object with useful methods.
       
   496     return {
       
   497         snapshot: heap,
       
   498 
       
   499         /**
       
   500          * Class iteration: Calls callback function for each
       
   501          * Java Class in the heap. Default callback function 
       
   502          * is 'print'. If callback returns true, the iteration 
       
   503          * is stopped.
       
   504          *
       
   505          * @param callback function to be called.
       
   506          */
       
   507         forEachClass: function(callback) {
       
   508             if (callback == undefined) callback = print;
       
   509             var classes = this.snapshot.classes;
       
   510             while (classes.hasMoreElements()) {
       
   511                 if (callback(wrapJavaValue(classes.nextElement())))
       
   512                     return;
       
   513             }
       
   514         },
       
   515 
       
   516         /**
       
   517          * Returns an Enumeration of all roots.
       
   518          */
       
   519         roots: function() {
       
   520             var e = this.snapshot.roots;
       
   521             return new java.util.Enumeration() {
       
   522                 hasMoreElements: function() {
       
   523                     return e.hasMoreElements();
       
   524                 },
       
   525                 nextElement: function() {
       
   526                     return wrapRoot(e.nextElement());
       
   527                 }
       
   528             };
       
   529         },
       
   530 
       
   531         /**
       
   532          * Returns an Enumeration for all Java classes.
       
   533          */
       
   534         classes: function() {
       
   535             return wrapIterator(this.snapshot.classes, true);
       
   536         },
       
   537 
       
   538         /**
       
   539          * Object iteration: Calls callback function for each
       
   540          * Java Object in the heap. Default callback function 
       
   541          * is 'print'.If callback returns true, the iteration 
       
   542          * is stopped.
       
   543          *
       
   544          * @param callback function to be called. 
       
   545          * @param clazz Class whose objects are retrieved.
       
   546          *        Optional, default is 'java.lang.Object'
       
   547          * @param includeSubtypes flag to tell if objects of subtypes
       
   548          *        are included or not. optional, default is true.
       
   549          */
       
   550         forEachObject: function(callback, clazz, includeSubtypes) {
       
   551             if (includeSubtypes == undefined) includeSubtypes = true;
       
   552             if (callback == undefined) callback = print;
       
   553             clazz = getClazz(clazz);
       
   554 
       
   555             if (clazz) {
       
   556                 var instances = clazz.getInstances(includeSubtypes);
       
   557                 while (instances.hasNextElements()) {
       
   558                     if (callback(wrapJavaValue(instances.nextElement())))
       
   559                         return;
       
   560                 }
       
   561             }
       
   562         },
       
   563 
       
   564         /** 
       
   565          * Returns an enumeration of Java objects in the heap.
       
   566          * 
       
   567          * @param clazz Class whose objects are retrieved.
       
   568          *        Optional, default is 'java.lang.Object'
       
   569          * @param includeSubtypes flag to tell if objects of subtypes
       
   570          *        are included or not. optional, default is true.
       
   571          * @param where (optional) filter expression or function to
       
   572          *        filter the objects. The expression has to return true
       
   573          *        to include object passed to it in the result array. 
       
   574          *        Built-in variable 'it' refers to the current object in 
       
   575          *        filter expression.
       
   576          */
       
   577         objects: function(clazz, includeSubtypes, where) {
       
   578             if (includeSubtypes == undefined) includeSubtypes = true;
       
   579             if (where) {
       
   580                 if (typeof(where) == 'string') {
       
   581                     where = new Function("it", "return " + where);
       
   582                 }
       
   583             }
       
   584             clazz = getClazz(clazz);
       
   585             if (clazz) {
       
   586                 var instances = clazz.getInstances(includeSubtypes);
       
   587                 if (where) {
       
   588                     return filterEnumeration(instances, where, true);
       
   589                 } else {
       
   590                     return wrapperEnumeration(instances);
       
   591                 }
       
   592             } else {
       
   593                 return emptyEnumeration;
       
   594             }
       
   595         },
       
   596 
       
   597         /**
       
   598          * Find Java Class of given name.
       
   599          * 
       
   600          * @param name class name
       
   601          */
       
   602         findClass: function(name) {
       
   603             var clazz = this.snapshot.findClass(name + '');
       
   604             return wrapJavaValue(clazz);
       
   605         },
       
   606 
       
   607         /**
       
   608          * Find Java Object from given object id
       
   609          *
       
   610          * @param id object id as string
       
   611          */
       
   612         findObject: function(id) {
       
   613             return wrapJavaValue(this.snapshot.findThing(id));
       
   614         },
       
   615 
       
   616         /**
       
   617          * Returns an enumeration of objects in the finalizer
       
   618          * queue waiting to be finalized.
       
   619          */
       
   620         finalizables: function() {
       
   621             var tmp = this.snapshot.getFinalizerObjects();
       
   622             return wrapperEnumeration(tmp);
       
   623         },
       
   624  
       
   625         /**
       
   626          * Returns an array that contains objects referred from the
       
   627          * given Java object directly or indirectly (i.e., all 
       
   628          * transitively referred objects are returned).
       
   629          *
       
   630          * @param jobject Java object whose reachables are returned.
       
   631          */
       
   632         reachables: function (jobject) {
       
   633             return reachables(jobject, this.snapshot.reachableExcludes);
       
   634         },
       
   635 
       
   636         /**
       
   637          * Returns array of paths of references by which the given 
       
   638          * Java object is live. Each path itself is an array of
       
   639          * objects in the chain of references. Each path supports
       
   640          * toHtml method that returns html description of the path.
       
   641          *
       
   642          * @param jobject Java object whose live paths are returned
       
   643          * @param weak flag to indicate whether to include paths with
       
   644          *             weak references or not. default is false.
       
   645          */
       
   646         livepaths: function (jobject, weak) {
       
   647             if (weak == undefined) {
       
   648                 weak = false;
       
   649             }
       
   650 
       
   651             function wrapRefChain(refChain) {
       
   652                 var path = new Array();
       
   653 
       
   654                 // compute path array from refChain
       
   655                 var tmp = refChain;
       
   656                 while (tmp != null) {
       
   657                     var obj = tmp.obj;
       
   658                     path[path.length] = wrapJavaValue(obj);
       
   659                     tmp = tmp.next;
       
   660                 }
       
   661 
       
   662                 function computeDescription(html) {
       
   663                     var root = refChain.obj.root;
       
   664                     var desc = root.description;
       
   665                     if (root.referer) {
       
   666                         var ref = root.referer;
       
   667                         desc += " (from " + 
       
   668                             (html? toHtml(ref) : ref.toString()) + ')';
       
   669                     }
       
   670                     desc += '->';
       
   671                     var tmp = refChain;
       
   672                     while (tmp != null) {
       
   673                         var next = tmp.next;
       
   674                         var obj = tmp.obj;
       
   675                         desc += html? toHtml(obj) : obj.toString();
       
   676                         if (next != null) {
       
   677                             desc += " (" + 
       
   678                                     obj.describeReferenceTo(next.obj, heap)  + 
       
   679                                     ") ->";
       
   680                         }
       
   681                         tmp = next;
       
   682                     }
       
   683                     return desc;
       
   684                 }
       
   685 
       
   686                 return new JSAdapter() {
       
   687                     __getIds__ : function() {
       
   688                         var res = new Array(path.length);
       
   689                         for (var i = 0; i < path.length; i++) {
       
   690                             res[i] = String(i);
       
   691                         }
       
   692                         return res;
       
   693                     },
       
   694                     __has__ : function (name) {
       
   695                         return (name >= 0 && name < path.length) ||
       
   696                             name == 'length' || name == 'toHtml' ||
       
   697                             name == 'toString';
       
   698                     },
       
   699                     __get__ : function(name) {
       
   700                         if (name >= 0 && name < path.length) {
       
   701                             return path[name];
       
   702                         } else if (name == 'length') {
       
   703                             return path.length;
       
   704                         } else {
       
   705                             return undefined;
       
   706                         }
       
   707                     },
       
   708                     __call__: function(name) {
       
   709                         if (name == 'toHtml') {
       
   710                             return computeDescription(true);
       
   711                         } else if (name == 'toString') {
       
   712                             return computeDescription(false);
       
   713                         } else {
       
   714                             return undefined;
       
   715                         }
       
   716                     }
       
   717                 };
       
   718             }
       
   719 
       
   720             jobject = unwrapJavaObject(jobject);
       
   721             var refChains = this.snapshot.rootsetReferencesTo(jobject, weak);
       
   722             var paths = new Array(refChains.length);
       
   723             for (var i in refChains) {
       
   724                 paths[i] = wrapRefChain(refChains[i]);
       
   725             }
       
   726             return paths;
       
   727         },
       
   728 
       
   729         /**
       
   730          * Return description string for reference from 'from' object
       
   731          * to 'to' Java object.
       
   732          *
       
   733          * @param from source Java object
       
   734          * @param to destination Java object
       
   735          */
       
   736         describeRef: function (from, to) {
       
   737             from = unwrapJavaObject(from);
       
   738             to = unwrapJavaObject(to);
       
   739             return from.describeReferenceTo(to, this.snapshot);
       
   740         },
       
   741     };
       
   742 }
       
   743 
       
   744 // per-object functions
       
   745 
       
   746 /**
       
   747  * Returns allocation site trace (if available) of a Java object
       
   748  *
       
   749  * @param jobject object whose allocation site trace is returned
       
   750  */
       
   751 function allocTrace(jobject) {
       
   752     try {
       
   753         jobject = unwrapJavaObject(jobject);			
       
   754         var trace = jobject.allocatedFrom;
       
   755         return (trace != null) ? trace.frames : null;
       
   756     } catch (e) {
       
   757         print("allocTrace: " + jobject + ", " + e);
       
   758         return null;
       
   759     }
       
   760 }
       
   761 
       
   762 /**
       
   763  * Returns Class object for given Java object
       
   764  *
       
   765  * @param jobject object whose Class object is returned
       
   766  */
       
   767 function classof(jobject) {
       
   768     jobject = unwrapJavaObject(jobject);
       
   769     return wrapJavaValue(jobject.clazz);
       
   770 }
       
   771 
       
   772 /**
       
   773  * Find referers (a.k.a in-coming references). Calls callback
       
   774  * for each referrer of the given Java object. If the callback 
       
   775  * returns true, the iteration is stopped.
       
   776  *
       
   777  * @param callback function to call for each referer
       
   778  * @param jobject object whose referers are retrieved
       
   779  */
       
   780 function forEachReferrer(callback, jobject) {
       
   781     jobject = unwrapJavaObject(jobject);
       
   782     var refs = jobject.referers;
       
   783     while (refs.hasMoreElements()) {
       
   784         if (callback(wrapJavaValue(refs.nextElement()))) {
       
   785             return;
       
   786         }
       
   787     }
       
   788 }
       
   789 
       
   790 /**
       
   791  * Compares two Java objects for object identity.
       
   792  *
       
   793  * @param o1, o2 objects to compare for identity
       
   794  */
       
   795 function identical(o1, o2) {
       
   796     return objectid(o1) == objectid(o2);
       
   797 }
       
   798 
       
   799 /**
       
   800  * Returns Java object id as string
       
   801  *
       
   802  * @param jobject object whose id is returned
       
   803  */
       
   804 function objectid(jobject) {
       
   805     try {
       
   806         jobject = unwrapJavaObject(jobject);
       
   807         return String(jobject.idString);
       
   808     } catch (e) {
       
   809         print("objectid: " + jobject + ", " + e);
       
   810         return null;
       
   811     }
       
   812 }
       
   813 
       
   814 /**
       
   815  * Prints allocation site trace of given object
       
   816  *
       
   817  * @param jobject object whose allocation site trace is returned
       
   818  */
       
   819 function printAllocTrace(jobject) {
       
   820     var frames = this.allocTrace(jobject);
       
   821     if (frames == null || frames.length == 0) {
       
   822         print("allocation site trace unavailable for " + 
       
   823               objectid(jobject));
       
   824         return;
       
   825     }    
       
   826     print(objectid(jobject) + " was allocated at ..");
       
   827     for (var i in frames) {
       
   828         var frame = frames[i];
       
   829         var src = frame.sourceFileName;
       
   830         if (src == null) src = '<unknown source>';
       
   831         print('\t' + frame.className + "." +
       
   832              frame.methodName + '(' + frame.methodSignature + ') [' +
       
   833              src + ':' + frame.lineNumber + ']');
       
   834     }
       
   835 }
       
   836 
       
   837 /**
       
   838  * Returns an enumeration of referrers of the given Java object.
       
   839  *
       
   840  * @param jobject Java object whose referrers are returned.
       
   841  */
       
   842 function referrers(jobject) {
       
   843     try {
       
   844         jobject = unwrapJavaObject(jobject);
       
   845         return wrapperEnumeration(jobject.referers);
       
   846     } catch (e) {
       
   847         print("referrers: " + jobject + ", " + e);
       
   848         return emptyEnumeration;
       
   849     }
       
   850 }
       
   851 
       
   852 /**
       
   853  * Returns an array that contains objects referred from the
       
   854  * given Java object.
       
   855  *
       
   856  * @param jobject Java object whose referees are returned.
       
   857  */
       
   858 function referees(jobject) {
       
   859     var res = new Array();
       
   860     jobject = unwrapJavaObject(jobject);
       
   861     if (jobject != undefined) {
       
   862         try {
       
   863             jobject.visitReferencedObjects(
       
   864                 new hatPkg.model.JavaHeapObjectVisitor() {
       
   865                     visit: function(other) { 
       
   866                         res[res.length] = wrapJavaValue(other);
       
   867                     },
       
   868                     exclude: function(clazz, field) { 
       
   869                         return false; 
       
   870                     },
       
   871                     mightExclude: function() { 
       
   872                         return false; 
       
   873                     }
       
   874                 });
       
   875         } catch (e) {
       
   876             print("referees: " + jobject + ", " + e);
       
   877         }
       
   878     }
       
   879     return res;
       
   880 }
       
   881 
       
   882 /**
       
   883  * Returns an array that contains objects referred from the
       
   884  * given Java object directly or indirectly (i.e., all 
       
   885  * transitively referred objects are returned).
       
   886  *
       
   887  * @param jobject Java object whose reachables are returned.
       
   888  * @param excludes optional comma separated list of fields to be 
       
   889  *                 removed in reachables computation. Fields are
       
   890  *                 written as class_name.field_name form.
       
   891  */
       
   892 function reachables(jobject, excludes) {
       
   893     if (excludes == undefined) {
       
   894         excludes = null;
       
   895     } else if (typeof(excludes) == 'string') {
       
   896         var st = new java.util.StringTokenizer(excludes, ",");
       
   897         var excludedFields = new Array();
       
   898         while (st.hasMoreTokens()) {
       
   899             excludedFields[excludedFields.length] = st.nextToken().trim();
       
   900         }
       
   901         if (excludedFields.length > 0) { 
       
   902             excludes = new hatPkg.model.ReachableExcludes() {
       
   903                         isExcluded: function (field) {
       
   904                             for (var index in excludedFields) {
       
   905                                 if (field.equals(excludedFields[index])) {
       
   906                                     return true;
       
   907                                 }
       
   908                             }
       
   909                             return false;
       
   910                         }
       
   911                     };
       
   912         } else {
       
   913             // nothing to filter...
       
   914             excludes = null;
       
   915         }
       
   916     } else if (! (excludes instanceof hatPkg.model.ReachableExcludes)) {
       
   917         excludes = null;
       
   918     }
       
   919 
       
   920     jobject = unwrapJavaObject(jobject);
       
   921     var ro = new hatPkg.model.ReachableObjects(jobject, excludes);  
       
   922     var tmp = ro.reachables;
       
   923     var res = new Array(tmp.length);
       
   924     for (var i in tmp) {
       
   925         res[i] = wrapJavaValue(tmp[i]);
       
   926     }
       
   927     return res;
       
   928 }
       
   929 
       
   930 
       
   931 /**
       
   932  * Returns whether 'from' object refers to 'to' object or not.
       
   933  *
       
   934  * @param from Java object that is source of the reference.
       
   935  * @param to Java object that is destination of the reference.
       
   936  */
       
   937 function refers(from, to) {
       
   938     try {
       
   939         var tmp = unwrapJavaObject(from);
       
   940         if (tmp instanceof hatPkg.model.JavaClass) {
       
   941             from = from.statics;
       
   942         } else if (tmp instanceof hatPkg.model.JavaValueArray) {
       
   943             return false;
       
   944         }
       
   945         for (var i in from) {
       
   946             if (identical(from[i], to)) {
       
   947                 return true;
       
   948             }
       
   949         }
       
   950     } catch (e) {
       
   951         print("refers: " + from + ", " + e);
       
   952     }
       
   953     return false;
       
   954 }
       
   955 
       
   956 /**
       
   957  * If rootset includes given jobject, return Root
       
   958  * object explanining the reason why it is a root.
       
   959  *
       
   960  * @param jobject object whose Root is returned
       
   961  */
       
   962 function root(jobject) {
       
   963     try {
       
   964         jobject = unwrapJavaObject(jobject);
       
   965         return wrapRoot(jobject.root);
       
   966     } catch (e) {
       
   967         return null;
       
   968     }
       
   969 }
       
   970 
       
   971 /**
       
   972  * Returns size of the given Java object
       
   973  *
       
   974  * @param jobject object whose size is returned
       
   975  */
       
   976 function sizeof(jobject) {
       
   977     try {
       
   978         jobject = unwrapJavaObject(jobject);
       
   979         return jobject.size;
       
   980     } catch (e) {
       
   981         print("sizeof: " + jobject + ", " + e);
       
   982         return null;
       
   983     }
       
   984 }
       
   985 
       
   986 /**
       
   987  * Returns String by replacing Unicode chars and
       
   988  * HTML special chars (such as '<') with entities.
       
   989  *
       
   990  * @param str string to be encoded
       
   991  */
       
   992 function encodeHtml(str) {
       
   993     return hatPkg.util.Misc.encodeHtml(str);
       
   994 }
       
   995 
       
   996 /**
       
   997  * Returns HTML string for the given object.
       
   998  *
       
   999  * @param obj object for which HTML string is returned.
       
  1000  */
       
  1001 function toHtml(obj) {
       
  1002     if (obj == null) {
       
  1003         return "null";
       
  1004     } 
       
  1005 
       
  1006     if (obj == undefined) {
       
  1007         return "undefined";
       
  1008     } 
       
  1009 
       
  1010     var tmp = unwrapJavaObject(obj);
       
  1011     if (tmp != undefined) {
       
  1012         var id = tmp.idString;
       
  1013         if (tmp instanceof Packages.com.sun.tools.hat.internal.model.JavaClass) {
       
  1014             var name = tmp.name;
       
  1015             return "<a href='/class/" + id + "'>class " + name + "</a>";
       
  1016         } else {
       
  1017             var name = tmp.clazz.name;
       
  1018             return "<a href='/object/" + id + "'>" +
       
  1019                    name + "@" + id + "</a>";
       
  1020         }
       
  1021     } else if (obj instanceof Object) {
       
  1022         if (Array.isArray(obj)) {
       
  1023             // script array
       
  1024             var res = "[ ";
       
  1025             for (var i in obj) {
       
  1026                 res += toHtml(obj[i]);
       
  1027                 if (i != obj.length - 1) {
       
  1028                     res += ", ";
       
  1029                 }
       
  1030             } 
       
  1031             res += " ]";
       
  1032             return res;
       
  1033         } else {
       
  1034             // if the object has a toHtml function property
       
  1035             // just use that...
       
  1036             if (typeof(obj.toHtml) == 'function') {
       
  1037                 return obj.toHtml();
       
  1038             } else {
       
  1039                 // script object
       
  1040                 var res = "{ ";
       
  1041                 for (var i in obj) {
       
  1042                     res +=  i + ":" + toHtml(obj[i]) + ", ";
       
  1043                 }
       
  1044                 res += "}";
       
  1045                 return res;
       
  1046             }
       
  1047         }
       
  1048     } else {
       
  1049         // a Java object
       
  1050         obj = wrapIterator(obj);
       
  1051         // special case for enumeration
       
  1052         if (obj instanceof java.util.Enumeration) {
       
  1053             var res = "[ ";
       
  1054             while (obj.hasMoreElements()) {
       
  1055                 res += toHtml(obj.nextElement()) + ", ";
       
  1056             }
       
  1057             res += "]";
       
  1058             return res; 
       
  1059         } else {
       
  1060             return obj;
       
  1061         }
       
  1062     }
       
  1063 }
       
  1064 
       
  1065 /*
       
  1066  * Generic array/iterator/enumeration [or even object!] manipulation 
       
  1067  * functions. These functions accept an array/iteration/enumeration
       
  1068  * and expression String or function. These functions iterate each 
       
  1069  * element of array and apply the expression/function on each element.
       
  1070  */
       
  1071 
       
  1072 // private function to wrap an Iterator as an Enumeration
       
  1073 function wrapIterator(itr, wrap) {
       
  1074     if (itr instanceof java.util.Iterator) {
       
  1075         return new java.util.Enumeration() {
       
  1076                    hasMoreElements: function() {
       
  1077                        return itr.hasNext();
       
  1078                    },
       
  1079                    nextElement: function() {
       
  1080                        return wrap? wrapJavaValue(itr.next()) : itr.next();
       
  1081                    }
       
  1082                };
       
  1083     } else {
       
  1084         return itr;
       
  1085     }
       
  1086 }
       
  1087 
       
  1088 /**
       
  1089  * Converts an enumeration/iterator/object into an array
       
  1090  *
       
  1091  * @param obj enumeration/iterator/object
       
  1092  * @return array that contains values of enumeration/iterator/object
       
  1093  */
       
  1094 function toArray(obj) {	
       
  1095     obj = wrapIterator(obj);
       
  1096     if (obj instanceof java.util.Enumeration) {
       
  1097         var res = new Array();
       
  1098         while (obj.hasMoreElements()) {
       
  1099             res[res.length] = obj.nextElement();
       
  1100         }
       
  1101         return res;
       
  1102     } else if (obj instanceof Array) {
       
  1103         return obj;
       
  1104     } else {
       
  1105         var res = new Array();
       
  1106         for (var index in obj) {
       
  1107             res[res.length] = obj[index];
       
  1108         }
       
  1109         return res;
       
  1110     }
       
  1111 }
       
  1112 
       
  1113 /**
       
  1114  * Returns whether the given array/iterator/enumeration contains 
       
  1115  * an element that satisfies the given boolean expression specified 
       
  1116  * in code. 
       
  1117  *
       
  1118  * @param array input array/iterator/enumeration that is iterated
       
  1119  * @param code  expression string or function 
       
  1120  * @return boolean result
       
  1121  *
       
  1122  * The code evaluated can refer to the following built-in variables. 
       
  1123  *
       
  1124  * 'it' -> currently visited element
       
  1125  * 'index' -> index of the current element
       
  1126  * 'array' -> array that is being iterated
       
  1127  */
       
  1128 function contains(array, code) {
       
  1129     array = wrapIterator(array);
       
  1130     var func = code;
       
  1131     if (typeof(func) != 'function') {
       
  1132         func = new Function("it", "index", "array",  "return " + code);
       
  1133     }
       
  1134 
       
  1135     if (array instanceof java.util.Enumeration) {
       
  1136         var index = 0;
       
  1137         while (array.hasMoreElements()) {
       
  1138             var it = array.nextElement();
       
  1139             if (func(it, index, array)) {
       
  1140                 return true;
       
  1141             }
       
  1142             index++;
       
  1143         }
       
  1144     } else {
       
  1145         for (var index in array) {
       
  1146             var it = array[index];
       
  1147             if (func(it, String(index), array)) {
       
  1148                 return true;
       
  1149             }
       
  1150         }
       
  1151     }
       
  1152     return false;
       
  1153 }
       
  1154 
       
  1155 /**
       
  1156  * concatenates two arrays/iterators/enumerators.
       
  1157  *
       
  1158  * @param array1 array/iterator/enumeration
       
  1159  * @param array2 array/iterator/enumeration
       
  1160  *
       
  1161  * @return concatenated array or composite enumeration
       
  1162  */
       
  1163 function concat(array1, array2) {
       
  1164     array1 = wrapIterator(array1);
       
  1165     array2 = wrapIterator(array2);
       
  1166     if (array1 instanceof Array && array2 instanceof Array) {
       
  1167         return array1.concat(array2);
       
  1168     } else if (array1 instanceof java.util.Enumeration &&
       
  1169                array2 instanceof java.util.Enumeration) {
       
  1170         return new Packages.com.sun.tools.hat.internal.util.CompositeEnumeration(array1, array2);
       
  1171     } else {
       
  1172         return undefined;
       
  1173     }
       
  1174 }
       
  1175 
       
  1176 /**
       
  1177  * Returns the number of array/iterator/enumeration elements 
       
  1178  * that satisfy the given boolean expression specified in code. 
       
  1179  * The code evaluated can refer to the following built-in variables. 
       
  1180  *
       
  1181  * @param array input array/iterator/enumeration that is iterated
       
  1182  * @param code  expression string or function 
       
  1183  * @return number of elements
       
  1184  *
       
  1185  * 'it' -> currently visited element
       
  1186  * 'index' -> index of the current element
       
  1187  * 'array' -> array that is being iterated
       
  1188  */
       
  1189 function count(array, code) {
       
  1190     if (code == undefined) {
       
  1191         return length(array);
       
  1192     }
       
  1193     array = wrapIterator(array);
       
  1194     var func = code;
       
  1195     if (typeof(func) != 'function') {
       
  1196         func = new Function("it", "index", "array",  "return " + code);
       
  1197     }
       
  1198 
       
  1199     var result = 0;
       
  1200     if (array instanceof java.util.Enumeration) {
       
  1201         var index = 0;
       
  1202         while (array.hasMoreElements()) {
       
  1203             var it = array.nextElement();
       
  1204             if (func(it, index, array)) {
       
  1205                 result++;
       
  1206             }
       
  1207             index++;
       
  1208         }
       
  1209     } else {
       
  1210         for (var index in array) {
       
  1211             var it = array[index];
       
  1212             if (func(it, index, array)) {
       
  1213                 result++;
       
  1214             }
       
  1215         }
       
  1216     }
       
  1217     return result;
       
  1218 }
       
  1219 
       
  1220 /**
       
  1221  * filter function returns an array/enumeration that contains 
       
  1222  * elements of the input array/iterator/enumeration that satisfy 
       
  1223  * the given boolean expression. The boolean expression code can 
       
  1224  * refer to the following built-in variables. 
       
  1225  *
       
  1226  * @param array input array/iterator/enumeration that is iterated
       
  1227  * @param code  expression string or function 
       
  1228  * @return array/enumeration that contains the filtered elements
       
  1229  *
       
  1230  * 'it' -> currently visited element
       
  1231  * 'index' -> index of the current element
       
  1232  * 'array' -> array that is being iterated
       
  1233  * 'result' -> result array
       
  1234  */
       
  1235 function filter(array, code) {
       
  1236     array = wrapIterator(array);
       
  1237     var func = code;
       
  1238     if (typeof(code) != 'function') {
       
  1239         func = new Function("it", "index", "array", "result", "return " + code);
       
  1240     }
       
  1241     if (array instanceof java.util.Enumeration) {
       
  1242         return filterEnumeration(array, func, false);
       
  1243     } else {
       
  1244         var result = new Array();
       
  1245         for (var index in array) {
       
  1246             var it = array[index];
       
  1247             if (func(it, String(index), array, result)) {
       
  1248                 result[result.length] = it;
       
  1249             }
       
  1250         }
       
  1251         return result;
       
  1252     }
       
  1253 }
       
  1254 
       
  1255 /**
       
  1256  * Returns the number of elements of array/iterator/enumeration.
       
  1257  *
       
  1258  * @param array input array/iterator/enumeration that is iterated
       
  1259  */
       
  1260 function length(array) {
       
  1261     array = wrapIterator(array);
       
  1262     if (array instanceof Array) {
       
  1263         return array.length;
       
  1264     } else if (array instanceof java.util.Enumeration) {
       
  1265         var cnt = 0;
       
  1266         while (array.hasMoreElements()) {
       
  1267             array.nextElement(); 
       
  1268             cnt++;
       
  1269         }
       
  1270         return cnt;
       
  1271     } else {
       
  1272         var cnt = 0;
       
  1273         for (var index in array) {
       
  1274             cnt++;
       
  1275         }
       
  1276         return cnt;
       
  1277     }
       
  1278 }
       
  1279 
       
  1280 /**
       
  1281  * Transforms the given object or array by evaluating given code
       
  1282  * on each element of the object or array. The code evaluated
       
  1283  * can refer to the following built-in variables. 
       
  1284  *
       
  1285  * @param array input array/iterator/enumeration that is iterated
       
  1286  * @param code  expression string or function 
       
  1287  * @return array/enumeration that contains mapped values
       
  1288  *
       
  1289  * 'it' -> currently visited element
       
  1290  * 'index' -> index of the current element
       
  1291  * 'array' -> array that is being iterated
       
  1292  * 'result' -> result array
       
  1293  *
       
  1294  * map function returns an array/enumeration of values created 
       
  1295  * by repeatedly calling code on each element of the input
       
  1296  * array/iterator/enumeration.
       
  1297  */
       
  1298 function map(array, code) {
       
  1299     array = wrapIterator(array);
       
  1300     var func = code;
       
  1301     if(typeof(code) != 'function') {
       
  1302         func = new Function("it", "index", "array", "result", "return " + code);
       
  1303     }
       
  1304 
       
  1305     if (array instanceof java.util.Enumeration) {
       
  1306         var index = 0;
       
  1307         var result = new java.util.Enumeration() {
       
  1308             hasMoreElements: function() {
       
  1309                 return array.hasMoreElements();
       
  1310             },
       
  1311             nextElement: function() {
       
  1312                 return func(array.nextElement(), index++, array, result);
       
  1313             }
       
  1314         };
       
  1315         return result;
       
  1316     } else {
       
  1317         var result = new Array();
       
  1318         for (var index in array) {
       
  1319             var it = array[index];
       
  1320             result[result.length] = func(it, String(index), array, result);
       
  1321         }
       
  1322         return result;
       
  1323     }
       
  1324 }
       
  1325 
       
  1326 // private function used by min, max functions
       
  1327 function minmax(array, code) {
       
  1328     if (typeof(code) == 'string') {
       
  1329         code = new Function("lhs", "rhs", "return " + code);
       
  1330     }
       
  1331     array = wrapIterator(array);
       
  1332     if (array instanceof java.util.Enumeration) {
       
  1333         if (! array.hasMoreElements()) {
       
  1334             return undefined;
       
  1335         }
       
  1336         var res = array.nextElement();
       
  1337         while (array.hasMoreElements()) {
       
  1338             var next = array.nextElement();
       
  1339             if (code(next, res)) {
       
  1340                 res = next;
       
  1341             }
       
  1342         }
       
  1343         return res;
       
  1344     } else {
       
  1345         if (array.length == 0) {
       
  1346             return undefined;
       
  1347         }
       
  1348         var res = array[0];
       
  1349         for (var index = 1; index < array.length; index++) {
       
  1350             if (code(array[index], res)) {
       
  1351                 res = array[index];
       
  1352             }
       
  1353         } 
       
  1354         return res;
       
  1355     }
       
  1356 }
       
  1357 
       
  1358 /**
       
  1359  * Returns the maximum element of the array/iterator/enumeration
       
  1360  *
       
  1361  * @param array input array/iterator/enumeration that is iterated
       
  1362  * @param code (optional) comparision expression or function
       
  1363  *        by default numerical maximum is computed.
       
  1364  */
       
  1365 function max(array, code) {
       
  1366     if (code == undefined) {
       
  1367         code = function (lhs, rhs) { return lhs > rhs; }
       
  1368     }
       
  1369     return minmax(array, code);
       
  1370 }
       
  1371 
       
  1372 /**
       
  1373  * Returns the minimum element of the array/iterator/enumeration
       
  1374  *
       
  1375  * @param array input array/iterator/enumeration that is iterated
       
  1376  * @param code (optional) comparision expression or function
       
  1377  *        by default numerical minimum is computed.
       
  1378  */
       
  1379 function min(array, code) {
       
  1380     if (code == undefined) {
       
  1381         code = function (lhs, rhs) { return lhs < rhs; }
       
  1382     } 
       
  1383     return minmax(array, code);
       
  1384 }
       
  1385 
       
  1386 /**
       
  1387  * sort function sorts the input array. optionally accepts
       
  1388  * code to compare the elements. If code is not supplied,
       
  1389  * numerical sort is done.
       
  1390  *
       
  1391  * @param array input array/iterator/enumeration that is sorted
       
  1392  * @param code  expression string or function 
       
  1393  * @return sorted array 
       
  1394  *
       
  1395  * The comparison expression can refer to the following
       
  1396  * built-in variables:
       
  1397  *
       
  1398  * 'lhs' -> 'left side' element
       
  1399  * 'rhs' -> 'right side' element
       
  1400  */
       
  1401 function sort(array, code) {
       
  1402     // we need an array to sort, so convert non-arrays
       
  1403     array = toArray(array);
       
  1404 
       
  1405     // by default use numerical comparison
       
  1406     var func = code;
       
  1407     if (code == undefined) {
       
  1408         func = function(lhs, rhs) { return lhs - rhs; };
       
  1409     } else if (typeof(code) == 'string') {
       
  1410         func = new Function("lhs", "rhs", "return " + code);
       
  1411     }
       
  1412     return array.sort(func);
       
  1413 }
       
  1414 
       
  1415 /**
       
  1416  * Returns the sum of the elements of the array
       
  1417  *
       
  1418  * @param array input array that is summed.
       
  1419  * @param code optional expression used to map
       
  1420  *        input elements before sum.
       
  1421  */
       
  1422 function sum(array, code) {
       
  1423     array = wrapIterator(array);
       
  1424     if (code != undefined) {
       
  1425         array = map(array, code);
       
  1426     }
       
  1427     var result = 0;
       
  1428     if (array instanceof java.util.Enumeration) {
       
  1429         while (array.hasMoreElements()) {
       
  1430             result += Number(array.nextElement());
       
  1431         }
       
  1432     } else {
       
  1433         for (var index in array) {
       
  1434             result += Number(array[index]);
       
  1435         }
       
  1436     }
       
  1437     return result;
       
  1438 }
       
  1439 
       
  1440 /**
       
  1441  * Returns array of unique elements from the given input 
       
  1442  * array/iterator/enumeration.
       
  1443  *
       
  1444  * @param array from which unique elements are returned.
       
  1445  * @param code optional expression (or function) giving unique
       
  1446  *             attribute/property for each element.
       
  1447  *             by default, objectid is used for uniqueness.
       
  1448  */
       
  1449 function unique(array, code) {
       
  1450     array = wrapIterator(array);
       
  1451     if (code == undefined) {
       
  1452         code = new Function("it", "return objectid(it);");
       
  1453     } else if (typeof(code) == 'string') {
       
  1454         code = new Function("it", "return " + code);
       
  1455     }
       
  1456     var tmp = new Object();
       
  1457     if (array instanceof java.util.Enumeration) {
       
  1458         while (array.hasMoreElements()) {
       
  1459             var it = array.nextElement();
       
  1460             tmp[code(it)] = it;
       
  1461         }
       
  1462     } else {
       
  1463         for (var index in array) {
       
  1464             var it = array[index];
       
  1465             tmp[code(it)] = it;
       
  1466         }
       
  1467     }
       
  1468     var res = new Array();
       
  1469     for (var index in tmp) {
       
  1470         res[res.length] = tmp[index];
       
  1471     }
       
  1472     return res;
       
  1473 }