diff agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaScriptEngine.java @ 0:a61af66fc99e jdk7-b24

Initial load
author duke
date Sat, 01 Dec 2007 00:00:00 +0000
parents
children c18cbe5936b8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaScriptEngine.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,648 @@
+/*
+ * Copyright 2004-2007 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.
+ *
+ * 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.
+ *
+ */
+
+package sun.jvm.hotspot.utilities.soql;
+
+import java.io.*;
+import java.util.*;
+import javax.script.Invocable;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import sun.jvm.hotspot.debugger.*;
+import sun.jvm.hotspot.oops.*;
+import sun.jvm.hotspot.runtime.*;
+import sun.jvm.hotspot.utilities.*;
+import sun.jvm.hotspot.tools.*;
+import sun.jvm.hotspot.tools.jcore.*;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * Simple wrapper around jsr-223 JavaScript script engine.
+ * In addition to wrapping useful functionality of jsr-223 engine,
+ * this class exposed certain "global" functions to the script.
+ */
+public abstract class JSJavaScriptEngine extends MapScriptObject {
+    /**
+     * Start a read-eval-print loop with this engine.
+     */
+    public void startConsole() {
+      start(true);
+    }
+
+    /**
+     * Initialize the engine so that we can "eval" strings
+     * and files later.
+     */
+    public void start() {
+      start(false);
+    }
+
+    /**
+     * Define a global function that invokes given Method.
+     */
+    public void defineFunction(Object target, Method method) {
+      putFunction(target, method, false);
+    }
+
+    /**
+     * Call the script function of given name passing the
+     * given arguments.
+     */
+    public Object call(String name, Object[] args) {
+      Invocable invocable = (Invocable)engine;
+      try {
+        return invocable.invokeFunction(name, args);
+      } catch (RuntimeException re) {
+        throw re;
+      } catch (Exception exp) {
+        throw new RuntimeException(exp);
+      }
+    }
+
+    /**
+       address function returns address of JSJavaObject as String. For other
+       type of objects, the result is undefined.
+    */
+    public Object address(Object[] args) {
+        if (args.length != 1) return UNDEFINED;
+        Object o = args[0];
+        if (o != null && o instanceof JSJavaObject) {
+            return ((JSJavaObject)o).getOop().getHandle().toString();
+        } else {
+            return UNDEFINED;
+        }
+    }
+
+
+    /**
+       classof function gets type of given JSJavaInstance or JSJavaArray. Or
+       given a string class name, this function gets the class object. For
+       other type of objects, the result is undefined.
+    */
+    public Object classof(Object[] args) {
+        if (args.length != 1) {
+            return UNDEFINED;
+        }
+        Object o = args[0];
+        if (o != null) {
+            if (o instanceof JSJavaObject) {
+                if (o instanceof JSJavaInstance) {
+                    return ((JSJavaInstance)o).getJSJavaClass();
+                } else if (o instanceof JSJavaArray) {
+                    return ((JSJavaArray)o).getJSJavaClass();
+                } else {
+                    return UNDEFINED;
+                }
+            } else if (o instanceof String) {
+                InstanceKlass ik = SystemDictionaryHelper.findInstanceKlass((String) o);
+                return getJSJavaFactory().newJSJavaKlass(ik).getJSJavaClass();
+            } else {
+                return UNDEFINED;
+            }
+        } else {
+            return UNDEFINED;
+        }
+    }
+
+    /**
+     * dumpClass function creates a .class file for a given Class object.
+     * On success, returns true. Else, returns false. Second optional argument
+     * specifies the directory in which .class content is dumped. This defaults
+     * to '.'
+    */
+    public Object dumpClass(Object[] args) {
+        if (args.length == 0) {
+            return Boolean.FALSE;
+        }
+        Object clazz = args[0];
+      if (clazz == null) {
+          return Boolean.FALSE;
+      }
+        InstanceKlass ik = null;
+        if (clazz instanceof String) {
+            String name = (String) clazz;
+            if (name.startsWith("0x")) {
+                // treat it as address
+                VM vm = VM.getVM();
+                Address addr = vm.getDebugger().parseAddress(name);
+                Oop oop = vm.getObjectHeap().newOop(addr.addOffsetToAsOopHandle(0));
+                if (oop instanceof InstanceKlass) {
+                    ik = (InstanceKlass) oop;
+                } else {
+                    return Boolean.FALSE;
+                }
+            } else {
+                ik = SystemDictionaryHelper.findInstanceKlass((String) clazz);
+            }
+        } else if (clazz instanceof JSJavaClass) {
+            JSJavaKlass jk = ((JSJavaClass)clazz).getJSJavaKlass();
+            if (jk != null && jk instanceof JSJavaInstanceKlass) {
+                ik = ((JSJavaInstanceKlass)jk).getInstanceKlass();
+            }
+        } else {
+            return Boolean.FALSE;
+        }
+
+        if (ik == null) return Boolean.FALSE;
+        StringBuffer buf = new StringBuffer();
+        if (args.length > 1) {
+            buf.append(args[1].toString());
+        } else {
+            buf.append('.');
+        }
+
+        buf.append(File.separatorChar);
+        buf.append(ik.getName().asString().replace('/', File.separatorChar));
+        buf.append(".class");
+        String fileName = buf.toString();
+        File file = new File(fileName);
+
+        try {
+            int index = fileName.lastIndexOf(File.separatorChar);
+            File dir = new File(fileName.substring(0, index));
+            dir.mkdirs();
+            FileOutputStream fos = new FileOutputStream(file);
+            ClassWriter cw = new ClassWriter(ik, fos);
+            cw.write();
+            fos.close();
+        } catch (IOException exp) {
+            printError(exp.toString(), exp);
+            return Boolean.FALSE;
+        }
+
+        return Boolean.TRUE;
+    }
+
+    /**
+     * dumpHeap function creates a heap dump file.
+     * On success, returns true. Else, returns false.
+    */
+    public Object dumpHeap(Object[] args) {
+        String fileName = "heap.bin";
+        if (args.length > 0) {
+            fileName = args[0].toString();
+        }
+        return new JMap().writeHeapHprofBin(fileName)? Boolean.TRUE: Boolean.FALSE;
+    }
+
+    /**
+        help function prints help message for global functions and variables.
+    */
+    public void help(Object[] args) {
+        println("Function/Variable        Description");
+        println("=================        ===========");
+        println("address(jobject)         returns the address of the Java object");
+        println("classof(jobject)         returns the class object of the Java object");
+        println("dumpClass(jclass,[dir])  writes .class for the given Java Class");
+        println("dumpHeap([file])         writes heap in hprof binary format");
+        println("help()                   prints this help message");
+        println("identityHash(jobject)    returns the hashCode of the Java object");
+        println("mirror(jobject)          returns a local mirror of the Java object");
+        println("load([file1, file2,...]) loads JavaScript file(s). With no files, reads <stdin>");
+        println("object(string)           converts a string address into Java object");
+        println("owner(jobject)           returns the owner thread of this monitor or null");
+        println("sizeof(jobject)          returns the size of Java object in bytes");
+        println("staticof(jclass, field)  returns a static field of the given Java class");
+        println("read([prompt])           reads a single line from standard input");
+        println("quit()                   quits the interactive load call");
+        println("jvm                      the target jvm that is being debugged");
+    }
+
+    /**
+       identityHash function gets identity hash code value of given
+       JSJavaObject. For other type of objects, the result is undefined.
+    */
+    public Object identityHash(Object[] args) {
+        if (args.length != 1) return UNDEFINED;
+        Object o = args[0];
+        if (o != null && o instanceof JSJavaObject) {
+            return new Long(((JSJavaObject)o).getOop().identityHash());
+        } else {
+            return UNDEFINED;
+        }
+    }
+
+
+    /**
+     * Load and execute a set of JavaScript source files.
+     * This method is defined as a JavaScript function.
+     */
+    public void load(Object[] args) {
+       for (int i = 0; i < args.length; i++) {
+         processSource(args[i].toString());
+       }
+    }
+
+    /**
+       mirror function creats local copy of the Oop wrapper supplied.
+       if mirror can not be created, return undefined. For other types,
+       mirror is undefined.
+    */
+    public Object mirror(Object[] args) {
+        Object o = args[0];
+        if (o != null && o instanceof JSJavaObject) {
+            Oop oop = ((JSJavaObject)o).getOop();
+            Object res = null;
+            try {
+                if (oop instanceof InstanceKlass) {
+                    res = getObjectReader().readClass((InstanceKlass) oop);
+                } else {
+                    res = getObjectReader().readObject(oop);
+                }
+            } catch (Exception e) {
+                if (debug) e.printStackTrace(getErrorStream());
+            }
+            return (res != null)? res : UNDEFINED;
+        } else {
+            return UNDEFINED;
+        }
+    }
+
+    /**
+       owner function gets owning thread of given JSJavaObjec, if any, else
+       returns null. For other type of objects, the result is undefined.
+    */
+    public Object owner(Object[] args) {
+        Object o = args[0];
+        if (o != null && o instanceof JSJavaObject) {
+            return getOwningThread((JSJavaObject)o);
+        } else {
+            return UNDEFINED;
+        }
+    }
+
+    /**
+       object function takes a string address and returns a JSJavaObject.
+       For other type of objects, the result is undefined.
+    */
+    public Object object(Object[] args) {
+        Object o = args[0];
+        if (o != null && o instanceof String) {
+            VM vm = VM.getVM();
+            Address addr = vm.getDebugger().parseAddress((String)o);
+            Oop oop = vm.getObjectHeap().newOop(addr.addOffsetToAsOopHandle(0));
+            return getJSJavaFactory().newJSJavaObject(oop);
+        } else {
+            return UNDEFINED;
+        }
+    }
+
+    /**
+       sizeof function returns size of a Java object in bytes. For other type
+       of objects, the result is undefined.
+    */
+    public Object sizeof(Object[] args) {
+        if (args.length != 1) return UNDEFINED;
+        Object o = args[0];
+        if (o != null && o instanceof JSJavaObject) {
+            return new Long(((JSJavaObject)o).getOop().getObjectSize());
+        } else {
+            return UNDEFINED;
+        }
+    }
+
+    /**
+       staticof function gets static field of given class. Both class and
+       field name are specified as strings. undefined is returned if there is
+       no such named field.
+    */
+    public Object staticof(Object[] args) {
+        Object classname = args[0];
+        Object fieldname = args[1];
+        if (fieldname == null || classname == null ||
+            !(fieldname instanceof String)) {
+            return UNDEFINED;
+        }
+
+        InstanceKlass ik = null;
+        if (classname instanceof JSJavaClass) {
+            JSJavaClass jclass = (JSJavaClass) classname;
+            JSJavaKlass jk = jclass.getJSJavaKlass();
+            if (jk != null && jk instanceof JSJavaInstanceKlass) {
+                ik = ((JSJavaInstanceKlass)jk).getInstanceKlass();
+            }
+        } else if (classname instanceof String) {
+            ik = SystemDictionaryHelper.findInstanceKlass((String)classname);
+        } else {
+            return UNDEFINED;
+        }
+
+        if (ik == null) {
+            return UNDEFINED;
+        }
+        JSJavaFactory factory = getJSJavaFactory();
+        try {
+            return ((JSJavaInstanceKlass) factory.newJSJavaKlass(ik)).getStaticFieldValue((String)fieldname);
+        } catch (NoSuchFieldException e) {
+            return UNDEFINED;
+        }
+    }
+
+    /**
+     * read function reads a single line of input from standard input
+    */
+    public Object read(Object[] args) {
+        BufferedReader in = getInputReader();
+      if (in == null) {
+        return null;
+      }
+        if (args.length > 0) {
+          print(args[0].toString());
+          print(":");
+        }
+        try {
+          return in.readLine();
+        } catch (IOException exp) {
+        exp.printStackTrace();
+          throw new RuntimeException(exp);
+        }
+    }
+
+    /**
+     * Quit the shell.
+     * This only affects the interactive mode.
+     */
+    public void quit(Object[] args) {
+        quit();
+    }
+
+    public void writeln(Object[] args) {
+      for (int i = 0; i < args.length; i++) {
+        print(args[i].toString());
+        print(" ");
+      }
+      println("");
+    }
+
+    public void write(Object[] args) {
+      for (int i = 0; i < args.length; i++) {
+        print(args[i].toString());
+        print(" ");
+      }
+    }
+
+    //-- Internals only below this point
+    protected void start(boolean console) {
+      ScriptContext context = engine.getContext();
+      OutputStream out = getOutputStream();
+      if (out != null) {
+        context.setWriter(new PrintWriter(out));
+      }
+      OutputStream err = getErrorStream();
+      if (err != null) {
+        context.setErrorWriter(new PrintWriter(err));
+      }
+      // load "sa.js" initialization file
+      loadInitFile();
+      // load "~/jsdb.js" (if found) to perform user specific
+      // initialization steps, if any.
+      loadUserInitFile();
+
+      JSJavaFactory fac = getJSJavaFactory();
+      JSJavaVM jvm = (fac != null)? fac.newJSJavaVM() : null;
+      // call "main" function from "sa.js" -- main expects
+      // 'this' object and jvm object
+      call("main", new Object[] { this, jvm });
+
+      // if asked, start read-eval-print console
+      if (console) {
+        processSource(null);
+      }
+    }
+
+    protected JSJavaScriptEngine(boolean debug) {
+        this.debug = debug;
+      ScriptEngineManager manager = new ScriptEngineManager();
+      engine = manager.getEngineByName("javascript");
+      if (engine == null) {
+        throw new RuntimeException("can't load JavaScript engine");
+      }
+      Method[] methods = getClass().getMethods();
+      for (int i = 0; i < methods.length; i++) {
+        Method m = methods[i];
+        if (! Modifier.isPublic(m.getModifiers())) {
+          continue;
+        }
+        Class[] argTypes = m.getParameterTypes();
+        if (argTypes.length == 1 &&
+            argTypes[0] == Object[].class) {
+          putFunction(this, m);
+        }
+      }
+    }
+
+    protected JSJavaScriptEngine() {
+        this(false);
+    }
+
+    protected abstract ObjectReader getObjectReader();
+    protected abstract JSJavaFactory getJSJavaFactory();
+    protected void printPrompt(String str) {
+        System.err.print(str);
+        System.err.flush();
+    }
+
+    protected void loadInitFile() {
+      InputStream is = JSJavaScriptEngine.class.getResourceAsStream("sa.js");
+      BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+      evalReader(reader, "sa.js");
+    }
+
+    protected void loadUserInitFile() {
+        File initFile = new File(getUserInitFileDir(), getUserInitFileName());
+        if (initFile.exists() && initFile.isFile()) {
+        // load the init script
+          processSource(initFile.getAbsolutePath());
+        }
+    }
+
+    protected String getUserInitFileDir() {
+        return System.getProperty("user.home");
+    }
+
+    protected String getUserInitFileName() {
+        return "jsdb.js";
+    }
+
+    protected BufferedReader getInputReader() {
+      if (inReader == null) {
+        inReader = new BufferedReader(new InputStreamReader(System.in));
+      }
+      return inReader;
+    }
+
+    protected PrintStream getOutputStream() {
+      return System.out;
+    }
+
+    protected PrintStream getErrorStream() {
+      return System.err;
+    }
+
+    protected void print(String name) {
+      getOutputStream().print(name);
+    }
+
+    protected void println(String name) {
+      getOutputStream().println(name);
+    }
+
+    protected void printError(String message) {
+        printError(message, null);
+    }
+
+    protected void printError(String message, Exception exp) {
+        getErrorStream().println(message);
+        if (exp != null && debug) {
+          exp.printStackTrace(getErrorStream());
+        }
+    }
+
+    protected boolean isQuitting() {
+        return quitting;
+    }
+
+    protected void quit() {
+        quitting = true;
+    }
+
+    protected ScriptEngine getScriptEngine() {
+      return engine;
+    }
+
+    private JSJavaThread getOwningThread(JSJavaObject jo) {
+        Oop oop = jo.getOop();
+        Mark mark = oop.getMark();
+        ObjectMonitor mon = null;
+      Address owner = null;
+        JSJavaThread owningThread = null;
+        // check for heavyweight monitor
+        if (! mark.hasMonitor()) {
+            // check for lightweight monitor
+            if (mark.hasLocker()) {
+                owner = mark.locker().getAddress(); // save the address of the Lock word
+            }
+            // implied else: no owner
+        } else {
+            // this object has a heavyweight monitor
+            mon = mark.monitor();
+
+            // The owner field of a heavyweight monitor may be NULL for no
+            // owner, a JavaThread * or it may still be the address of the
+            // Lock word in a JavaThread's stack. A monitor can be inflated
+            // by a non-owning JavaThread, but only the owning JavaThread
+            // can change the owner field from the Lock word to the
+            // JavaThread * and it may not have done that yet.
+            owner = mon.owner();
+        }
+
+        // find the owning thread
+        if (owner != null) {
+            JSJavaFactory factory = getJSJavaFactory();
+            owningThread = (JSJavaThread) factory.newJSJavaThread(VM.getVM().getThreads().owningThreadFromMonitor(owner));
+        }
+        return owningThread;
+    }
+
+    /**
+     * Evaluate JavaScript source.
+     * @param filename the name of the file to compile, or null
+     *                 for interactive mode.
+     */
+    private void processSource(String filename) {
+        if (filename == null) {
+            BufferedReader in = getInputReader();
+            String sourceName = "<stdin>";
+            int lineno = 0;
+            boolean hitEOF = false;
+            do {
+                int startline = lineno;
+                printPrompt("jsdb> ");
+                Object source = read(EMPTY_ARRAY);
+                if (source == null) {
+                   hitEOF = true;
+                   break;
+                }
+                lineno++;
+                Object result = evalString(source.toString(), sourceName, startline);
+                if (result != null) {
+                    printError(result.toString());
+                }
+                if (isQuitting()) {
+                    // The user executed the quit() function.
+                    break;
+                }
+            } while (!hitEOF);
+        } else {
+            Reader in = null;
+            try {
+                in = new BufferedReader(new FileReader(filename));
+                evalReader(in, filename);
+            } catch (FileNotFoundException ex) {
+                println("File '" + filename + "' not found");
+                throw new RuntimeException(ex);
+            }
+        }
+    }
+
+    protected Object evalString(String source, String filename, int lineNum) {
+       try {
+         engine.put(ScriptEngine.FILENAME, filename);
+         return engine.eval(source);
+       } catch (ScriptException sexp) {
+         printError(sexp.toString(), sexp);
+         } catch (Exception exp) {
+         printError(exp.toString(), exp);
+       }
+       return null;
+    }
+
+    private Object evalReader(Reader in, String filename) {
+       try {
+         engine.put(ScriptEngine.FILENAME, filename);
+         return engine.eval(in);
+       } catch (ScriptException sexp) {
+         System.err.println(sexp);
+         printError(sexp.toString(), sexp);
+         } finally {
+         try {
+           in.close();
+         } catch (IOException ioe) {
+           printError(ioe.toString(), ioe);
+         }
+       }
+       return null;
+    }
+
+    // lazily initialized input reader
+    private BufferedReader inReader;
+    // debug mode or not
+    protected final boolean debug;
+    private boolean quitting;
+    // underlying jsr-223 script engine
+    private ScriptEngine engine;
+}