001 package daikon.chicory;
002
003 import java.util.regex.Pattern;
004 import java.util.zip.GZIPOutputStream;
005 import java.io.*;
006 import java.net.*;
007 import java.net.Socket;
008 import java.util.*;
009
010 /**
011 * Runtime support for Chicory, the Daikon front end for Java.
012 * This class is a collection of methods; it should never be instantiated.
013 */
014 public class Runtime
015 {
016 /** Unique id for method entry/exit (so they can be matched up) **/
017 public static int nonce = 0;
018
019 /** debug flag **/
020 public static final boolean debug = false;
021
022 /**
023 * Flag indicating that a dtrace record is currently being written
024 * used to prevent a call to instrumented code that occurs as part
025 * of generating a dtrace record (eg, toArray when processing lists
026 * or pure functions) from generating a nested dtrace record
027 */
028 public static boolean in_dtrace = false;
029
030 /** True if ChicoryPremain was unable to load. **/
031 public static boolean chicoryLoaderInstantiationError = false;
032
033 /**
034 * List of classes recently transformed. This list is examined in
035 * each enter/exit and the decl information for any new classes are
036 * printed out and the class is then removed from the list.
037 */
038 public static final List<ClassInfo> new_classes
039 = new LinkedList<ClassInfo>();
040
041 /** List of all instrumented classes **/
042 public static final List<ClassInfo> all_classes
043 = new ArrayList<ClassInfo>();
044
045 /** flag that indicates when the first class has been processed**/
046 static boolean first_class = true;
047
048 /** List of all instrumented methods **/
049 public static final List<MethodInfo> methods = new ArrayList<MethodInfo>();
050
051 //
052 // Control over what classes (ppts) are instrumented
053 //
054 /** Ppts to omit (regular expression) **/
055 public static List<Pattern> ppt_omit_pattern = new ArrayList<Pattern>();
056
057 /** Ppts to include (regular expression) **/
058 public static List<Pattern> ppt_select_pattern = new ArrayList<Pattern>();
059
060 /** Comparability information (if any) **/
061 static /*@Nullable*/ DeclReader comp_info = null;
062
063 //
064 // Setups that control what information is written
065 //
066 /** Render linked lists as vectors **/
067 static boolean linked_lists = true;
068
069 /** Depth to wich to examine structure components **/
070 static int nesting_depth = 2;
071
072 //
073 // Dtrace file vars
074 //
075 /** Max number of records in dtrace file **/
076 static long dtraceLimit = Long.MAX_VALUE;
077
078 /** Number of records printed to date **/
079 static long printedRecords = 0;
080
081 /** Terminate the program when the dtrace limit is reached **/
082 static boolean dtraceLimitTerminate = false;
083
084 /** Dtrace output stream **/
085 static PrintStream dtrace;
086
087 /** Set to true when the dtrace stream is closed **/
088 static boolean dtrace_closed = false;
089
090 /** True if no dtrace is being generated. **/
091 static boolean no_dtrace = false;
092
093 /** Decl writer setup for writing to the trace file **/
094 // Set in ChicoryPremain.premain().
095 static DeclWriter decl_writer;
096
097 /** Dtrace writer setup for writing to the trace file **/
098 // Set in ChicoryPremain.premain().
099 static DTraceWriter dtrace_writer;
100
101 /**
102 * Which static initializers have been run.
103 * Each element of the Set is a fully qualified class name.
104 **/
105 private static Set<String> initSet = new HashSet<String>();
106
107 /** Class of information about each active call **/
108 private static class CallInfo {
109 /** nonce of call **/
110 int nonce;
111 /** whether or not the call was captured on enter **/
112 boolean captured;
113 public CallInfo (int nonce, boolean captured) {
114 this.nonce = nonce; this.captured = captured;
115 }
116 }
117
118 /** Stack of active methods. **/
119 private static Map<Thread,Stack<CallInfo>> thread_to_callstack
120 = new LinkedHashMap<Thread,Stack<CallInfo>>();
121
122 /**
123 * Sample count at a call site to begin sampling. All previous calls
124 * will be recorded. Sampling starts at 10% and decreases by a factor
125 * of 10 each time another sample_start samples have been recorded. If
126 * sample_start is 0, then all calls will be recorded.
127 */
128 public static int sample_start = 0;
129
130 // Constructor
131 private Runtime()
132 {
133 throw new Error("Do not create instances of Runtime");
134 }
135
136 /** Printf to dtrace file. **/
137 final private static void printf(String format, /*@Nullable*/ Object... args)
138 {
139 if (!dtrace_closed)
140 dtrace.printf(format, args);
141 }
142
143 /** Println to dtrace file. **/
144 final private static void println(String msg)
145 {
146 if (!dtrace_closed)
147 dtrace.println(msg);
148 }
149
150 /** Println to dtrace file. **/
151 final private static void println(int val)
152 {
153 if (!dtrace_closed)
154 dtrace.println(val);
155 }
156
157 /** Println to dtrace file. **/
158 final private static void println(Object obj)
159 {
160 if (!dtrace_closed)
161 dtrace.println(obj);
162 }
163
164 /** Println to dtrace file. **/
165 final private static void println()
166 {
167 if (!dtrace_closed)
168 dtrace.println();
169 }
170
171 /**
172 * Thrown to indicate that main should not print a stack trace, but only
173 * print the message itself to the user.
174 * If the string is null, then this is normal termination, not an error.
175 **/
176 public static class TerminationMessage extends RuntimeException
177 {
178 static final long serialVersionUID = 20050923L;
179
180 public TerminationMessage(String s)
181 {
182 super(s);
183 }
184
185 public TerminationMessage()
186 {
187 super();
188 }
189 }
190
191
192 private static boolean invokingPure = false;
193 public static boolean dontProcessPpts()
194 {
195 return invokingPure;
196 }
197 public static void startPure()
198 {
199 invokingPure = true;
200 }
201 public static void endPure()
202 {
203 invokingPure = false;
204 }
205
206 /**
207 * Called when a method is entered.
208 *
209 * @param obj - Receiver of the method that was entered. Null if method is
210 * static
211 * @param nonce - Nonce identifying which enter/exit pair this is
212 * @param mi_index - Index in methods of the MethodInfo for this method
213 * @param args - Array of arguments to method
214 */
215 public static synchronized void enter(/*@Nullable*/ Object obj, int nonce, int mi_index,
216 Object[] args) {
217
218 if (dontProcessPpts())
219 return;
220
221 // Make sure that the in_dtrace flag matches the stack trace
222 // check_in_dtrace();
223
224 // Ignore this call if we are already processing a dtrace record
225 if (in_dtrace)
226 return;
227
228 // Note that we are processing a dtrace record until we return
229 in_dtrace = true;
230 try {
231 int num_new_classes = 0;
232 synchronized (new_classes) {
233 num_new_classes = new_classes.size();
234 }
235 if (num_new_classes > 0)
236 process_new_classes();
237
238 MethodInfo mi = methods.get(mi_index);
239 mi.call_cnt++;
240
241 // If sampling, check to see if we are capturing this sample
242 boolean capture = true;
243 if (sample_start > 0) {
244 if (mi.call_cnt <= sample_start)
245 ;
246 else if (mi.call_cnt <= (sample_start*10))
247 capture = (mi.call_cnt % 10) == 0;
248 else if (mi.call_cnt <= (sample_start*100))
249 capture = (mi.call_cnt % 100) == 0;
250 else if (mi.call_cnt <= (sample_start*1000))
251 capture = (mi.call_cnt % 1000) == 0;
252 else
253 capture = (mi.call_cnt % 10000) == 0;
254 Thread t = Thread.currentThread();
255 Stack<CallInfo> callstack = thread_to_callstack.get (t);
256 if (callstack == null) {
257 callstack = new Stack<CallInfo>();
258 thread_to_callstack.put (t, callstack);
259 }
260 callstack.push (new CallInfo (nonce, capture));
261 }
262
263 if (capture) {
264 mi.capture_cnt++;
265 // long start = System.currentTimeMillis();
266 dtrace_writer.methodEntry(mi, nonce, obj, args);
267 // long duration = System.currentTimeMillis() - start;
268 //System.out.println ("Enter " + mi + " " + duration + "ms"
269 // + " " + mi.capture_cnt + "/" + mi.call_cnt);
270 } else {
271 //System.out.println ("skipped " + mi
272 // + " " + mi.capture_cnt + "/" + mi.call_cnt);
273 }
274 } finally {
275 in_dtrace = false;
276 }
277 }
278
279 /**
280 * Called when a method is exited.
281 *
282 * @param obj - Receiver of the method that was entered. Null if method is
283 * static
284 * @param nonce - Nonce identifying which enter/exit pair this is
285 * @param mi_index - Index in methods of the MethodInfo for this method
286 * @param args - Array of arguments to method
287 * @param ret_val - Return value of method. null if method is void
288 * @param exitLineNum - The line number at which this method exited
289 */
290 public static synchronized void exit(/*@Nullable*/ Object obj, int nonce, int mi_index,
291 Object[] args, Object ret_val, int exitLineNum) {
292 if (dontProcessPpts())
293 return;
294
295 // Make sure that the in_dtrace flag matches the stack trace
296 // check_in_dtrace();
297
298 // Ignore this call if we are already processing a dtrace record
299 if (in_dtrace)
300 return;
301
302 // Note that we are processing a dtrace record until we return
303 in_dtrace = true;
304 try {
305
306 int num_new_classes = 0;
307 synchronized (new_classes) {
308 num_new_classes = new_classes.size();
309 }
310 if (num_new_classes > 0)
311 process_new_classes();
312
313 // Skip this call if it was not sampled at entry to the method
314 if (sample_start > 0) {
315 CallInfo ci = null;
316 @SuppressWarnings("nullness") // Map.get: key was put in map by enter()
317 /*@NonNull*/ Stack<CallInfo> callstack
318 = thread_to_callstack.get (Thread.currentThread());
319 while (!callstack.empty()) {
320 ci = callstack.pop();
321 if (ci.nonce == nonce)
322 break;
323 }
324 if (ci == null) {
325 System.out.printf ("no enter for exit %s%n", methods.get(mi_index));
326 return;
327 } else if (!ci.captured) {
328 return;
329 }
330 }
331
332 // Write out the infromation for this method
333 MethodInfo mi = methods.get(mi_index);
334 // long start = System.currentTimeMillis();
335 dtrace_writer.methodExit(mi, nonce, obj, args, ret_val,
336 exitLineNum);
337 // long duration = System.currentTimeMillis() - start;
338 // System.out.println ("Exit " + mi + " " + duration + "ms");
339 } finally {
340 in_dtrace = false;
341 }
342 }
343
344 /**
345 * Checks the in_dtrace flag by looking back up the stack trace.
346 * Throws an exception if there is a discrepancy.
347 */
348 private static void check_in_dtrace() {
349
350 Throwable st = new Throwable();
351 st.fillInStackTrace();
352 List<StackTraceElement> enter_exit_list
353 = new ArrayList<StackTraceElement>();
354 for (StackTraceElement ste : st.getStackTrace()) {
355 if (ste.getClassName().endsWith ("chicory.Runtime")
356 && (ste.getMethodName().equals ("enter")
357 || ste.getMethodName().equals ("exit")))
358 enter_exit_list.add (ste);
359 }
360 if (in_dtrace && (enter_exit_list.size() <= 1)) {
361 throw new RuntimeException ("in dtrace and stack contains "
362 + enter_exit_list);
363 } else if (!in_dtrace && (enter_exit_list.size() > 1)) {
364 throw new RuntimeException ("not in dtrace and stack contains "
365 + enter_exit_list);
366 }
367 }
368
369 /**
370 * Called by classes when they have finished initialization
371 * (i.e., their static initializer has completed).
372 *
373 * This functionality must be enabled by the flag
374 * Chicory.checkStaticInit. When enabled, this method should only
375 * be called by the hooks created in the Instrument class.
376 *
377 * @param className Fully qualified class name
378 */
379 public static void initNotify(String className)
380 {
381 assert !initSet.contains(className) : className + " already exists in initSet";
382
383 //System.out.println("initialized ---> " + name);
384 initSet.add(className);
385 }
386
387 /**
388 * Return true iff the class with fully qualified name className
389 * has been initialized.
390 *
391 * @param className Fully qualified class name
392 */
393 public static boolean isInitialized(String className)
394 {
395 return initSet.contains(className);
396 }
397
398 /**
399 * Writes out decl information for any new classes and removes
400 * them from the list.
401 */
402 public static void process_new_classes() {
403
404 // Processing of the new_classes list must be
405 // very careful, as the call to get_reflection or printDeclClass
406 // may load other classes (which then get added to the list).
407 while (true) {
408
409 // Get the first class in the list (if any)
410 ClassInfo class_info = null;
411 synchronized (new_classes) {
412 if (new_classes.size() > 0) {
413 class_info = new_classes.get (0);
414 new_classes.remove (0);
415 }
416 }
417 if (class_info == null)
418 break;
419
420 if (debug)
421 System.out.println ("processing class " + class_info.class_name);
422 if (first_class) {
423 decl_writer.printHeaderInfo (class_info.class_name);
424 first_class = false;
425 }
426 class_info.initViaReflection();
427 // class_info.dump (System.out);
428
429 // Create tree structure for all method entries/exits in the class
430 for (MethodInfo mi: class_info.method_infos)
431 {
432 mi.traversalEnter = RootInfo.enter_process(mi, Runtime.nesting_depth);
433 mi.traversalExit = RootInfo.exit_process(mi, Runtime.nesting_depth);
434 }
435
436 decl_writer.printDeclClass (class_info, comp_info);
437
438 }
439 }
440
441 /** Increment the number of records that have been printed. **/
442 public static void incrementRecords()
443 {
444 printedRecords++;
445
446 // This should only print a percentage if dtraceLimit is not its
447 // default value.
448 // if (printedRecords%1000 == 0)
449 // System.out.printf("printed=%d, percent printed=%f%n", printedRecords, (float)(100.0*(float)printedRecords/(float)dtraceLimit));
450
451 if (printedRecords >= dtraceLimit)
452 {
453 noMoreOutput();
454 }
455 }
456
457 /** Indicates that no more output should be printed to the dtrace file.
458 * The file is closed and iff dtraceLimitTerminate is true the program
459 * is terminated.
460 */
461 public static void noMoreOutput()
462 {
463 // The incrementRecords method (which calls this) is called inside a
464 // synchronized block, but re-synchronize just to be sure, or in case
465 // this is called from elsewhere.
466 synchronized (Runtime.dtrace)
467 {
468 // The shutdown hook is synchronized on this, so close it up
469 // ourselves, lest the call to System.exit cause deadlock.
470 dtrace.println();
471 dtrace.println("# EOF (added by no_more_output)");
472 dtrace.close();
473
474 // Don't set dtrace to null, because if we continue running, there will
475 // be many attempts to synchronize on it. (Is that a performance
476 // bottleneck, if we continue running?)
477 // dtrace = null;
478 dtrace_closed = true;
479
480
481 if (dtraceLimitTerminate)
482 {
483 System.out.println("Printed " + printedRecords + " records to dtrace file. Exiting.");
484 throw new TerminationMessage("Printed " + printedRecords + " records to dtrace file. Exiting.");
485 // System.exit(1);
486 }
487 else
488 {
489 // By default, no special output if the system continues to run.
490 no_dtrace = true;
491 }
492 }
493 }
494
495 public static void setDtraceOnlineMode(int port)
496 {
497 dtraceLimit = Long.getLong("DTRACELIMIT", Integer.MAX_VALUE).longValue();
498 dtraceLimitTerminate = Boolean.getBoolean("DTRACELIMITTERMINATE");
499
500 Socket daikonSocket = null;
501 try
502 {
503 daikonSocket = new Socket();
504 @SuppressWarnings("nullness") // unannotated: java.net.Socket is not yet annotated
505 /*@NonNull*/ SocketAddress dummy = null;
506 daikonSocket.bind(dummy);
507 //System.out.println("Attempting to connect to Daikon on port --- " + port);
508 daikonSocket.connect(new InetSocketAddress(InetAddress.getLocalHost(), port), 5000);
509 }
510 catch (UnknownHostException e)
511 {
512 System.out.println("UnknownHostException connecting to Daikon : " + e.getMessage() + ". Exiting");
513 System.exit(1);
514 }
515 catch (IOException e)
516 {
517 System.out.println("IOException, could not connect to Daikon : " + e.getMessage() + ". Exiting");
518 System.exit(1);
519 }
520
521 try
522 {
523 dtrace = new PrintStream(daikonSocket.getOutputStream());
524 }
525 catch (IOException e)
526 {
527 System.out.println("IOException connecting to Daikon : " + e.getMessage() + ". Exiting");
528 System.exit(1);
529 }
530
531 if (supportsAddShutdownHook())
532 {
533 addShutdownHook();
534 }
535 else
536 {
537 System.err.println("Warning: .dtrace file may be incomplete if program is aborted");
538 }
539 }
540
541 // Copied from daikon.Runtime
542 /** Specify the dtrace file to which to write **/
543 public static void setDtrace(String filename, boolean append)
544 {
545 System.out.printf("entered daikon.chicory.Runtime.setDtrace(%s, %b)...%n", filename, append);
546
547 if (no_dtrace)
548 {
549 throw new Error("setDtrace called when no_dtrace was specified");
550 }
551 try
552 {
553 File file = new File(filename);
554 File parent = file.getParentFile();
555 if (parent != null)
556 parent.mkdirs();
557 OutputStream os = new FileOutputStream(filename, append);
558 if (filename.endsWith(".gz"))
559 {
560 if (append)
561 throw new Error("DTRACEAPPEND environment variable is set, " + "Cannot append to gzipped dtrace file " + filename);
562 os = new GZIPOutputStream(os);
563 }
564 dtraceLimit = Long.getLong("DTRACELIMIT", Integer.MAX_VALUE).longValue();
565 dtraceLimitTerminate = Boolean.getBoolean("DTRACELIMITTERMINATE");
566
567 //System.out.println("limit = " + dtraceLimit + " terminate " + dtraceLimitTerminate);
568
569 // 8192 is the buffer size in BufferedReader
570 BufferedOutputStream bos = new BufferedOutputStream(os, 8192);
571 dtrace = new PrintStream(bos);
572 }
573 catch (Exception e)
574 {
575 e.printStackTrace();
576 throw new Error(e);
577 }
578 if (supportsAddShutdownHook())
579 {
580 addShutdownHook();
581 }
582 else
583 {
584 System.err.println("Warning: .dtrace file may be incomplete if program is aborted");
585 }
586 // System.out.printf("exited daikon.chicory.Runtime.setDtrace(%s, %b)%n", filename, append);
587 }
588
589 /**
590 * If the current data trace file is not yet set, then set it.
591 * The value of the DTRACEFILE environment variable is used;
592 * if that environment variable is not set, then the argument
593 * to this method is used instead.
594 **/
595 public static void setDtraceMaybe(String default_filename)
596 {
597 // Copied from daikon.Runtime
598 // System.out.println ("Setting dtrace maybe: " + default_filename);
599 if ((dtrace == null) && (!no_dtrace))
600 {
601 String filename = System.getProperty("DTRACEFILE", default_filename);
602 boolean append = System.getProperty("DTRACEAPPEND") != null;
603 setDtrace(filename, append);
604 }
605 }
606
607 private static boolean supportsAddShutdownHook()
608 {
609 // Copied from daikon.Runtime
610
611 try
612 {
613 Class<java.lang.Runtime> rt = java.lang.Runtime.class;
614 rt.getMethod("addShutdownHook", new Class<?>[]{java.lang.Thread.class});
615 return true;
616 }
617 catch (Exception e)
618 {
619 return false;
620 }
621 }
622
623 /**
624 * Add a shutdown hook to close the PrintStream when the program
625 * exits.
626 */
627 private static void addShutdownHook()
628 {
629 // Copied from daikon.Runtime, then modified
630
631 java.lang.Runtime.getRuntime().addShutdownHook(new Thread()
632 {
633 public void run()
634 {
635 if (!dtrace_closed)
636 {
637 // When the program being instrumented exits, the buffers
638 // of the "dtrace" (PrintStream) object are not flushed,
639 // so we miss the tail of the file.
640
641 synchronized (Runtime.dtrace)
642 {
643 dtrace.println();
644 // These are for debugging, I assume. -MDE
645 for (Pattern p : ppt_omit_pattern)
646 dtrace.println ("# ppt-omit-pattern: " + p);
647 for (Pattern p : ppt_select_pattern)
648 dtrace.println ("# ppt-select-pattern: " + p);
649 // This lets us know we didn't lose any data.
650 dtrace.println("# EOF (added by Runtime.addShutdownHook)");
651 dtrace.close();
652 }
653 }
654
655 if (chicoryLoaderInstantiationError) {
656 // Warning messages have already been printed.
657 } else if (all_classes.size() == 0) {
658 System.out.println ("Chicory warning: No methods were instrumented.");
659 if ((! ppt_select_pattern.isEmpty()) || (! ppt_omit_pattern.isEmpty())) {
660 System.out.println ("Check the --ppt-select-pattern and --ppt-omit-pattern options");
661 }
662 } else if (printedRecords == 0) {
663 System.out.println ("Chicory warning: no records were printed");
664 }
665 }
666 });
667 }
668
669 private static Process chicory_proc;
670
671 private static StreamRedirectThread err_thread;
672
673 private static StreamRedirectThread out_thread;
674
675
676 static void setDaikonInfo(StreamRedirectThread err, StreamRedirectThread out, Process proc)
677 {
678 chicory_proc = proc;
679 err_thread = err;
680 out_thread = out;
681 }
682
683 /**
684 * Wait for Daikon to terminate
685 */
686 public static void endDaikon()
687 {
688 try
689 {
690 int status = chicory_proc.waitFor();
691 System.out.println("daikon ended with status " + status);
692 }
693 catch (InterruptedException e1)
694 {
695 e1.printStackTrace();
696 }
697
698 try
699 {
700 err_thread.join();
701 out_thread.join();
702 }
703 catch(InterruptedException e)
704 {
705 }
706
707 System.out.println("Finished endDaikon");
708
709 }
710
711 /**
712 * Gets the ClassInfo structure corresponding to type. Returns null
713 * if the class was not instrumented.
714 * @param type declaring class
715 * @return ClassInfo structure corresponding to type
716 */
717 public static /*@Nullable*/ ClassInfo getClassInfoFromClass(Class<?> type)
718 {
719 try {
720 synchronized (Runtime.all_classes) {
721 for (ClassInfo cinfo : Runtime.all_classes) {
722 if (cinfo.clazz == null)
723 cinfo.initViaReflection();
724
725 if (cinfo.clazz.equals(type))
726 return cinfo;
727 }
728 }
729 }
730 catch(ConcurrentModificationException e) {
731 // occurs if cinfo.get_reflection() causes a new class to be loaded
732 // which causes all_classes to change
733 return getClassInfoFromClass(type);
734 }
735
736 // throw new RuntimeException("Unable to find class " + type.getName() + " in Runtime's class list");
737 return null;
738 }
739
740
741 ///////////////////////////////////////////////////////////////////////////
742 /// Wrappers for the various primitive types.
743 /// Used to distinguish wrappers created by user code
744 /// from wrappers created by Chicory.
745
746 public static interface PrimitiveWrapper
747 {
748 }
749
750 /** wrapper used for boolean arguments **/
751 public static class BooleanWrap implements PrimitiveWrapper{
752 boolean val;
753 public BooleanWrap (boolean val) { this.val = val; }
754 public String toString() {return Boolean.toString(val);}
755 }
756
757 /** wrapper used for int arguments **/
758 public static class ByteWrap implements PrimitiveWrapper{
759 byte val;
760 public ByteWrap (byte val) { this.val = val; }
761 public String toString() {return Byte.toString(val);}
762 }
763
764 /** wrapper used for int arguments **/
765 public static class CharWrap implements PrimitiveWrapper{
766 char val;
767 public CharWrap (char val) { this.val = val; }
768 // Print characters as integers.
769 public String toString() {return Integer.toString(val);}
770 }
771
772 /** wrapper used for int arguments **/
773 public static class FloatWrap implements PrimitiveWrapper{
774 float val;
775 public FloatWrap (float val) { this.val = val; }
776 public String toString() {return Float.toString(val);}
777 }
778
779 /** wrapper used for int arguments **/
780 public static class IntWrap implements PrimitiveWrapper{
781 int val;
782 public IntWrap (int val) { this.val = val; }
783 public String toString() {return Integer.toString(val);}
784 }
785
786 /** wrapper used for int arguments **/
787 public static class LongWrap implements PrimitiveWrapper{
788 long val;
789 public LongWrap (long val) { this.val = val; }
790 public String toString() {return Long.toString(val);}
791 }
792
793 /** wrapper used for int arguments **/
794 public static class ShortWrap implements PrimitiveWrapper{
795 short val;
796 public ShortWrap (short val) { this.val = val; }
797 public String toString() {return Short.toString(val);}
798 }
799
800 /** wrapper used for double arguments **/
801 public static class DoubleWrap implements PrimitiveWrapper{
802 double val;
803 public DoubleWrap (double val) { this.val = val; }
804 public String toString() {return Double.toString(val);}
805 }
806
807
808 ///////////////////////////////////////////////////////////////////////////
809 /// Copied code
810 ///
811
812 // Lifted directly from plume/UtilMDE.java, where it is called
813 // escapeNonJava(), but repeated here to make this class self-contained.
814 /** Quote \, ", \n, and \r characters in the target; return a new string. **/
815 public static String quote(String orig) {
816 StringBuffer sb = new StringBuffer();
817 // The previous escape (or escaped) character was seen right before
818 // this position. Alternately: from this character forward, the string
819 // should be copied out verbatim (until the next escaped character).
820 int post_esc = 0;
821 int orig_len = orig.length();
822 for (int i=0; i<orig_len; i++) {
823 char c = orig.charAt(i);
824 switch (c) {
825 case '\"':
826 case '\\':
827 if (post_esc < i) {
828 sb.append(orig.substring(post_esc, i));
829 }
830 sb.append('\\');
831 post_esc = i;
832 break;
833 case '\n': // not lineSep
834 if (post_esc < i) {
835 sb.append(orig.substring(post_esc, i));
836 }
837 sb.append("\\n"); // not lineSep
838 post_esc = i+1;
839 break;
840 case '\r':
841 if (post_esc < i) {
842 sb.append(orig.substring(post_esc, i));
843 }
844 sb.append("\\r");
845 post_esc = i+1;
846 break;
847 default:
848 // Do nothing; i gets incremented.
849 }
850 }
851 if (sb.length() == 0)
852 return orig;
853 sb.append(orig.substring(post_esc));
854 return sb.toString();
855 }
856
857 private static HashMap<String, String> primitiveClassesFromJvm = new HashMap<String, String>(8);
858 static {
859 primitiveClassesFromJvm.put("Z", "boolean");
860 primitiveClassesFromJvm.put("B", "byte");
861 primitiveClassesFromJvm.put("C", "char");
862 primitiveClassesFromJvm.put("D", "double");
863 primitiveClassesFromJvm.put("F", "float");
864 primitiveClassesFromJvm.put("I", "int");
865 primitiveClassesFromJvm.put("J", "long");
866 primitiveClassesFromJvm.put("S", "short");
867 }
868
869 /**
870 * Convert a classname from JVML format to Java format.
871 * For example, convert "[Ljava/lang/Object;" to "java.lang.Object[]".
872 **/
873 public static String classnameFromJvm(String classname) {
874
875 //System.out.println(classname);
876
877 int dims = 0;
878 while (classname.startsWith("[")) {
879 dims++;
880 classname = classname.substring(1);
881 }
882
883 String result;
884 //array of reference type
885 if (classname.startsWith("L") && classname.endsWith(";")) {
886 result = classname.substring(1, classname.length() - 1);
887 result = result.replace('/', '.');
888 }
889 else {
890 if (dims > 0) //array of primitives
891 result = primitiveClassesFromJvm.get(classname);
892 else //just a primitive
893 result = classname;
894
895 if (result == null) {
896 // As a failsafe, use the input; perhaps it is in Java, not JVML,
897 // format.
898 result = classname;
899 // throw new Error("Malformed base class: " + classname);
900 }
901 }
902 for (int i=0; i<dims; i++) {
903 result += "[]";
904 }
905 return result;
906 }
907
908 ///////////////////////////////////////////////////////////////////////////
909 /// end of copied code
910 ///
911
912 }