Mercurial > hg > truffle
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; +}