comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:a61af66fc99e
1 /*
2 * Copyright 2004-2007 Sun Microsystems, Inc. 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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 *
23 */
24
25 package sun.jvm.hotspot.utilities.soql;
26
27 import java.io.*;
28 import java.util.*;
29 import javax.script.Invocable;
30 import javax.script.ScriptContext;
31 import javax.script.ScriptEngine;
32 import javax.script.ScriptEngineManager;
33 import javax.script.ScriptException;
34 import sun.jvm.hotspot.debugger.*;
35 import sun.jvm.hotspot.oops.*;
36 import sun.jvm.hotspot.runtime.*;
37 import sun.jvm.hotspot.utilities.*;
38 import sun.jvm.hotspot.tools.*;
39 import sun.jvm.hotspot.tools.jcore.*;
40 import java.lang.reflect.Method;
41 import java.lang.reflect.Modifier;
42
43 /**
44 * Simple wrapper around jsr-223 JavaScript script engine.
45 * In addition to wrapping useful functionality of jsr-223 engine,
46 * this class exposed certain "global" functions to the script.
47 */
48 public abstract class JSJavaScriptEngine extends MapScriptObject {
49 /**
50 * Start a read-eval-print loop with this engine.
51 */
52 public void startConsole() {
53 start(true);
54 }
55
56 /**
57 * Initialize the engine so that we can "eval" strings
58 * and files later.
59 */
60 public void start() {
61 start(false);
62 }
63
64 /**
65 * Define a global function that invokes given Method.
66 */
67 public void defineFunction(Object target, Method method) {
68 putFunction(target, method, false);
69 }
70
71 /**
72 * Call the script function of given name passing the
73 * given arguments.
74 */
75 public Object call(String name, Object[] args) {
76 Invocable invocable = (Invocable)engine;
77 try {
78 return invocable.invokeFunction(name, args);
79 } catch (RuntimeException re) {
80 throw re;
81 } catch (Exception exp) {
82 throw new RuntimeException(exp);
83 }
84 }
85
86 /**
87 address function returns address of JSJavaObject as String. For other
88 type of objects, the result is undefined.
89 */
90 public Object address(Object[] args) {
91 if (args.length != 1) return UNDEFINED;
92 Object o = args[0];
93 if (o != null && o instanceof JSJavaObject) {
94 return ((JSJavaObject)o).getOop().getHandle().toString();
95 } else {
96 return UNDEFINED;
97 }
98 }
99
100
101 /**
102 classof function gets type of given JSJavaInstance or JSJavaArray. Or
103 given a string class name, this function gets the class object. For
104 other type of objects, the result is undefined.
105 */
106 public Object classof(Object[] args) {
107 if (args.length != 1) {
108 return UNDEFINED;
109 }
110 Object o = args[0];
111 if (o != null) {
112 if (o instanceof JSJavaObject) {
113 if (o instanceof JSJavaInstance) {
114 return ((JSJavaInstance)o).getJSJavaClass();
115 } else if (o instanceof JSJavaArray) {
116 return ((JSJavaArray)o).getJSJavaClass();
117 } else {
118 return UNDEFINED;
119 }
120 } else if (o instanceof String) {
121 InstanceKlass ik = SystemDictionaryHelper.findInstanceKlass((String) o);
122 return getJSJavaFactory().newJSJavaKlass(ik).getJSJavaClass();
123 } else {
124 return UNDEFINED;
125 }
126 } else {
127 return UNDEFINED;
128 }
129 }
130
131 /**
132 * dumpClass function creates a .class file for a given Class object.
133 * On success, returns true. Else, returns false. Second optional argument
134 * specifies the directory in which .class content is dumped. This defaults
135 * to '.'
136 */
137 public Object dumpClass(Object[] args) {
138 if (args.length == 0) {
139 return Boolean.FALSE;
140 }
141 Object clazz = args[0];
142 if (clazz == null) {
143 return Boolean.FALSE;
144 }
145 InstanceKlass ik = null;
146 if (clazz instanceof String) {
147 String name = (String) clazz;
148 if (name.startsWith("0x")) {
149 // treat it as address
150 VM vm = VM.getVM();
151 Address addr = vm.getDebugger().parseAddress(name);
152 Oop oop = vm.getObjectHeap().newOop(addr.addOffsetToAsOopHandle(0));
153 if (oop instanceof InstanceKlass) {
154 ik = (InstanceKlass) oop;
155 } else {
156 return Boolean.FALSE;
157 }
158 } else {
159 ik = SystemDictionaryHelper.findInstanceKlass((String) clazz);
160 }
161 } else if (clazz instanceof JSJavaClass) {
162 JSJavaKlass jk = ((JSJavaClass)clazz).getJSJavaKlass();
163 if (jk != null && jk instanceof JSJavaInstanceKlass) {
164 ik = ((JSJavaInstanceKlass)jk).getInstanceKlass();
165 }
166 } else {
167 return Boolean.FALSE;
168 }
169
170 if (ik == null) return Boolean.FALSE;
171 StringBuffer buf = new StringBuffer();
172 if (args.length > 1) {
173 buf.append(args[1].toString());
174 } else {
175 buf.append('.');
176 }
177
178 buf.append(File.separatorChar);
179 buf.append(ik.getName().asString().replace('/', File.separatorChar));
180 buf.append(".class");
181 String fileName = buf.toString();
182 File file = new File(fileName);
183
184 try {
185 int index = fileName.lastIndexOf(File.separatorChar);
186 File dir = new File(fileName.substring(0, index));
187 dir.mkdirs();
188 FileOutputStream fos = new FileOutputStream(file);
189 ClassWriter cw = new ClassWriter(ik, fos);
190 cw.write();
191 fos.close();
192 } catch (IOException exp) {
193 printError(exp.toString(), exp);
194 return Boolean.FALSE;
195 }
196
197 return Boolean.TRUE;
198 }
199
200 /**
201 * dumpHeap function creates a heap dump file.
202 * On success, returns true. Else, returns false.
203 */
204 public Object dumpHeap(Object[] args) {
205 String fileName = "heap.bin";
206 if (args.length > 0) {
207 fileName = args[0].toString();
208 }
209 return new JMap().writeHeapHprofBin(fileName)? Boolean.TRUE: Boolean.FALSE;
210 }
211
212 /**
213 help function prints help message for global functions and variables.
214 */
215 public void help(Object[] args) {
216 println("Function/Variable Description");
217 println("================= ===========");
218 println("address(jobject) returns the address of the Java object");
219 println("classof(jobject) returns the class object of the Java object");
220 println("dumpClass(jclass,[dir]) writes .class for the given Java Class");
221 println("dumpHeap([file]) writes heap in hprof binary format");
222 println("help() prints this help message");
223 println("identityHash(jobject) returns the hashCode of the Java object");
224 println("mirror(jobject) returns a local mirror of the Java object");
225 println("load([file1, file2,...]) loads JavaScript file(s). With no files, reads <stdin>");
226 println("object(string) converts a string address into Java object");
227 println("owner(jobject) returns the owner thread of this monitor or null");
228 println("sizeof(jobject) returns the size of Java object in bytes");
229 println("staticof(jclass, field) returns a static field of the given Java class");
230 println("read([prompt]) reads a single line from standard input");
231 println("quit() quits the interactive load call");
232 println("jvm the target jvm that is being debugged");
233 }
234
235 /**
236 identityHash function gets identity hash code value of given
237 JSJavaObject. For other type of objects, the result is undefined.
238 */
239 public Object identityHash(Object[] args) {
240 if (args.length != 1) return UNDEFINED;
241 Object o = args[0];
242 if (o != null && o instanceof JSJavaObject) {
243 return new Long(((JSJavaObject)o).getOop().identityHash());
244 } else {
245 return UNDEFINED;
246 }
247 }
248
249
250 /**
251 * Load and execute a set of JavaScript source files.
252 * This method is defined as a JavaScript function.
253 */
254 public void load(Object[] args) {
255 for (int i = 0; i < args.length; i++) {
256 processSource(args[i].toString());
257 }
258 }
259
260 /**
261 mirror function creats local copy of the Oop wrapper supplied.
262 if mirror can not be created, return undefined. For other types,
263 mirror is undefined.
264 */
265 public Object mirror(Object[] args) {
266 Object o = args[0];
267 if (o != null && o instanceof JSJavaObject) {
268 Oop oop = ((JSJavaObject)o).getOop();
269 Object res = null;
270 try {
271 if (oop instanceof InstanceKlass) {
272 res = getObjectReader().readClass((InstanceKlass) oop);
273 } else {
274 res = getObjectReader().readObject(oop);
275 }
276 } catch (Exception e) {
277 if (debug) e.printStackTrace(getErrorStream());
278 }
279 return (res != null)? res : UNDEFINED;
280 } else {
281 return UNDEFINED;
282 }
283 }
284
285 /**
286 owner function gets owning thread of given JSJavaObjec, if any, else
287 returns null. For other type of objects, the result is undefined.
288 */
289 public Object owner(Object[] args) {
290 Object o = args[0];
291 if (o != null && o instanceof JSJavaObject) {
292 return getOwningThread((JSJavaObject)o);
293 } else {
294 return UNDEFINED;
295 }
296 }
297
298 /**
299 object function takes a string address and returns a JSJavaObject.
300 For other type of objects, the result is undefined.
301 */
302 public Object object(Object[] args) {
303 Object o = args[0];
304 if (o != null && o instanceof String) {
305 VM vm = VM.getVM();
306 Address addr = vm.getDebugger().parseAddress((String)o);
307 Oop oop = vm.getObjectHeap().newOop(addr.addOffsetToAsOopHandle(0));
308 return getJSJavaFactory().newJSJavaObject(oop);
309 } else {
310 return UNDEFINED;
311 }
312 }
313
314 /**
315 sizeof function returns size of a Java object in bytes. For other type
316 of objects, the result is undefined.
317 */
318 public Object sizeof(Object[] args) {
319 if (args.length != 1) return UNDEFINED;
320 Object o = args[0];
321 if (o != null && o instanceof JSJavaObject) {
322 return new Long(((JSJavaObject)o).getOop().getObjectSize());
323 } else {
324 return UNDEFINED;
325 }
326 }
327
328 /**
329 staticof function gets static field of given class. Both class and
330 field name are specified as strings. undefined is returned if there is
331 no such named field.
332 */
333 public Object staticof(Object[] args) {
334 Object classname = args[0];
335 Object fieldname = args[1];
336 if (fieldname == null || classname == null ||
337 !(fieldname instanceof String)) {
338 return UNDEFINED;
339 }
340
341 InstanceKlass ik = null;
342 if (classname instanceof JSJavaClass) {
343 JSJavaClass jclass = (JSJavaClass) classname;
344 JSJavaKlass jk = jclass.getJSJavaKlass();
345 if (jk != null && jk instanceof JSJavaInstanceKlass) {
346 ik = ((JSJavaInstanceKlass)jk).getInstanceKlass();
347 }
348 } else if (classname instanceof String) {
349 ik = SystemDictionaryHelper.findInstanceKlass((String)classname);
350 } else {
351 return UNDEFINED;
352 }
353
354 if (ik == null) {
355 return UNDEFINED;
356 }
357 JSJavaFactory factory = getJSJavaFactory();
358 try {
359 return ((JSJavaInstanceKlass) factory.newJSJavaKlass(ik)).getStaticFieldValue((String)fieldname);
360 } catch (NoSuchFieldException e) {
361 return UNDEFINED;
362 }
363 }
364
365 /**
366 * read function reads a single line of input from standard input
367 */
368 public Object read(Object[] args) {
369 BufferedReader in = getInputReader();
370 if (in == null) {
371 return null;
372 }
373 if (args.length > 0) {
374 print(args[0].toString());
375 print(":");
376 }
377 try {
378 return in.readLine();
379 } catch (IOException exp) {
380 exp.printStackTrace();
381 throw new RuntimeException(exp);
382 }
383 }
384
385 /**
386 * Quit the shell.
387 * This only affects the interactive mode.
388 */
389 public void quit(Object[] args) {
390 quit();
391 }
392
393 public void writeln(Object[] args) {
394 for (int i = 0; i < args.length; i++) {
395 print(args[i].toString());
396 print(" ");
397 }
398 println("");
399 }
400
401 public void write(Object[] args) {
402 for (int i = 0; i < args.length; i++) {
403 print(args[i].toString());
404 print(" ");
405 }
406 }
407
408 //-- Internals only below this point
409 protected void start(boolean console) {
410 ScriptContext context = engine.getContext();
411 OutputStream out = getOutputStream();
412 if (out != null) {
413 context.setWriter(new PrintWriter(out));
414 }
415 OutputStream err = getErrorStream();
416 if (err != null) {
417 context.setErrorWriter(new PrintWriter(err));
418 }
419 // load "sa.js" initialization file
420 loadInitFile();
421 // load "~/jsdb.js" (if found) to perform user specific
422 // initialization steps, if any.
423 loadUserInitFile();
424
425 JSJavaFactory fac = getJSJavaFactory();
426 JSJavaVM jvm = (fac != null)? fac.newJSJavaVM() : null;
427 // call "main" function from "sa.js" -- main expects
428 // 'this' object and jvm object
429 call("main", new Object[] { this, jvm });
430
431 // if asked, start read-eval-print console
432 if (console) {
433 processSource(null);
434 }
435 }
436
437 protected JSJavaScriptEngine(boolean debug) {
438 this.debug = debug;
439 ScriptEngineManager manager = new ScriptEngineManager();
440 engine = manager.getEngineByName("javascript");
441 if (engine == null) {
442 throw new RuntimeException("can't load JavaScript engine");
443 }
444 Method[] methods = getClass().getMethods();
445 for (int i = 0; i < methods.length; i++) {
446 Method m = methods[i];
447 if (! Modifier.isPublic(m.getModifiers())) {
448 continue;
449 }
450 Class[] argTypes = m.getParameterTypes();
451 if (argTypes.length == 1 &&
452 argTypes[0] == Object[].class) {
453 putFunction(this, m);
454 }
455 }
456 }
457
458 protected JSJavaScriptEngine() {
459 this(false);
460 }
461
462 protected abstract ObjectReader getObjectReader();
463 protected abstract JSJavaFactory getJSJavaFactory();
464 protected void printPrompt(String str) {
465 System.err.print(str);
466 System.err.flush();
467 }
468
469 protected void loadInitFile() {
470 InputStream is = JSJavaScriptEngine.class.getResourceAsStream("sa.js");
471 BufferedReader reader = new BufferedReader(new InputStreamReader(is));
472 evalReader(reader, "sa.js");
473 }
474
475 protected void loadUserInitFile() {
476 File initFile = new File(getUserInitFileDir(), getUserInitFileName());
477 if (initFile.exists() && initFile.isFile()) {
478 // load the init script
479 processSource(initFile.getAbsolutePath());
480 }
481 }
482
483 protected String getUserInitFileDir() {
484 return System.getProperty("user.home");
485 }
486
487 protected String getUserInitFileName() {
488 return "jsdb.js";
489 }
490
491 protected BufferedReader getInputReader() {
492 if (inReader == null) {
493 inReader = new BufferedReader(new InputStreamReader(System.in));
494 }
495 return inReader;
496 }
497
498 protected PrintStream getOutputStream() {
499 return System.out;
500 }
501
502 protected PrintStream getErrorStream() {
503 return System.err;
504 }
505
506 protected void print(String name) {
507 getOutputStream().print(name);
508 }
509
510 protected void println(String name) {
511 getOutputStream().println(name);
512 }
513
514 protected void printError(String message) {
515 printError(message, null);
516 }
517
518 protected void printError(String message, Exception exp) {
519 getErrorStream().println(message);
520 if (exp != null && debug) {
521 exp.printStackTrace(getErrorStream());
522 }
523 }
524
525 protected boolean isQuitting() {
526 return quitting;
527 }
528
529 protected void quit() {
530 quitting = true;
531 }
532
533 protected ScriptEngine getScriptEngine() {
534 return engine;
535 }
536
537 private JSJavaThread getOwningThread(JSJavaObject jo) {
538 Oop oop = jo.getOop();
539 Mark mark = oop.getMark();
540 ObjectMonitor mon = null;
541 Address owner = null;
542 JSJavaThread owningThread = null;
543 // check for heavyweight monitor
544 if (! mark.hasMonitor()) {
545 // check for lightweight monitor
546 if (mark.hasLocker()) {
547 owner = mark.locker().getAddress(); // save the address of the Lock word
548 }
549 // implied else: no owner
550 } else {
551 // this object has a heavyweight monitor
552 mon = mark.monitor();
553
554 // The owner field of a heavyweight monitor may be NULL for no
555 // owner, a JavaThread * or it may still be the address of the
556 // Lock word in a JavaThread's stack. A monitor can be inflated
557 // by a non-owning JavaThread, but only the owning JavaThread
558 // can change the owner field from the Lock word to the
559 // JavaThread * and it may not have done that yet.
560 owner = mon.owner();
561 }
562
563 // find the owning thread
564 if (owner != null) {
565 JSJavaFactory factory = getJSJavaFactory();
566 owningThread = (JSJavaThread) factory.newJSJavaThread(VM.getVM().getThreads().owningThreadFromMonitor(owner));
567 }
568 return owningThread;
569 }
570
571 /**
572 * Evaluate JavaScript source.
573 * @param filename the name of the file to compile, or null
574 * for interactive mode.
575 */
576 private void processSource(String filename) {
577 if (filename == null) {
578 BufferedReader in = getInputReader();
579 String sourceName = "<stdin>";
580 int lineno = 0;
581 boolean hitEOF = false;
582 do {
583 int startline = lineno;
584 printPrompt("jsdb> ");
585 Object source = read(EMPTY_ARRAY);
586 if (source == null) {
587 hitEOF = true;
588 break;
589 }
590 lineno++;
591 Object result = evalString(source.toString(), sourceName, startline);
592 if (result != null) {
593 printError(result.toString());
594 }
595 if (isQuitting()) {
596 // The user executed the quit() function.
597 break;
598 }
599 } while (!hitEOF);
600 } else {
601 Reader in = null;
602 try {
603 in = new BufferedReader(new FileReader(filename));
604 evalReader(in, filename);
605 } catch (FileNotFoundException ex) {
606 println("File '" + filename + "' not found");
607 throw new RuntimeException(ex);
608 }
609 }
610 }
611
612 protected Object evalString(String source, String filename, int lineNum) {
613 try {
614 engine.put(ScriptEngine.FILENAME, filename);
615 return engine.eval(source);
616 } catch (ScriptException sexp) {
617 printError(sexp.toString(), sexp);
618 } catch (Exception exp) {
619 printError(exp.toString(), exp);
620 }
621 return null;
622 }
623
624 private Object evalReader(Reader in, String filename) {
625 try {
626 engine.put(ScriptEngine.FILENAME, filename);
627 return engine.eval(in);
628 } catch (ScriptException sexp) {
629 System.err.println(sexp);
630 printError(sexp.toString(), sexp);
631 } finally {
632 try {
633 in.close();
634 } catch (IOException ioe) {
635 printError(ioe.toString(), ioe);
636 }
637 }
638 return null;
639 }
640
641 // lazily initialized input reader
642 private BufferedReader inReader;
643 // debug mode or not
644 protected final boolean debug;
645 private boolean quitting;
646 // underlying jsr-223 script engine
647 private ScriptEngine engine;
648 }