1 37 package org.jruby; 38 39 import java.io.PrintStream ; 40 41 import org.jruby.runtime.Block; 42 import org.jruby.runtime.CallbackFactory; 43 import org.jruby.runtime.ObjectAllocator; 44 import org.jruby.runtime.builtin.IRubyObject; 45 46 50 public class RubyException extends RubyObject { 51 52 private RubyArray backtrace; 53 public IRubyObject message; 54 public static final int TRACE_HEAD = 8; 55 public static final int TRACE_TAIL = 4; 56 public static final int TRACE_MAX = TRACE_HEAD + TRACE_TAIL + 6; 57 58 protected RubyException(Ruby runtime, RubyClass rubyClass) { 59 this(runtime, rubyClass, null); 60 } 61 62 public RubyException(Ruby runtime, RubyClass rubyClass, String message) { 63 super(runtime, rubyClass); 64 65 this.message = message == null ? runtime.getNil() : runtime.newString(message); 66 } 67 68 private static ObjectAllocator EXCEPTION_ALLOCATOR = new ObjectAllocator() { 69 public IRubyObject allocate(Ruby runtime, RubyClass klass) { 70 RubyException instance = new RubyException(runtime, klass); 71 72 instance.setMetaClass(klass); 74 75 return instance; 76 } 77 }; 78 79 public static RubyClass createExceptionClass(Ruby runtime) { 80 RubyClass exceptionClass = runtime.defineClass("Exception", runtime.getObject(), EXCEPTION_ALLOCATOR); 81 82 CallbackFactory callbackFactory = runtime.callbackFactory(RubyException.class); 83 CallbackFactory classCB = runtime.callbackFactory(RubyClass.class); 84 exceptionClass.getMetaClass().defineMethod("exception", classCB.getOptMethod("newInstance")); 86 exceptionClass.defineMethod("initialize", callbackFactory.getOptMethod("initialize")); 87 exceptionClass.defineFastMethod("exception", callbackFactory.getFastOptMethod("exception")); 88 exceptionClass.defineFastMethod("to_s", callbackFactory.getFastMethod("to_s")); 89 exceptionClass.defineFastMethod("to_str", callbackFactory.getFastMethod("to_s")); 90 exceptionClass.defineFastMethod("message", callbackFactory.getFastMethod("to_s")); 91 exceptionClass.defineFastMethod("inspect", callbackFactory.getFastMethod("inspect")); 92 exceptionClass.defineFastMethod("backtrace", callbackFactory.getFastMethod("backtrace")); 93 exceptionClass.defineFastMethod("set_backtrace", callbackFactory.getFastMethod("set_backtrace", RubyKernel.IRUBY_OBJECT)); 94 95 return exceptionClass; 96 } 97 98 public static RubyException newException(Ruby runtime, RubyClass excptnClass, String msg) { 99 return new RubyException(runtime, excptnClass, msg); 100 } 101 102 public IRubyObject initialize(IRubyObject[] args, Block block) { 103 if (args.length > 0) { 104 message = args[0]; 105 } 106 return this; 107 } 108 109 public IRubyObject backtrace() { 110 return backtrace == null ? getRuntime().getNil() : backtrace; 111 } 112 113 public IRubyObject set_backtrace(IRubyObject obj) { 114 if (obj.isNil()) { 115 backtrace = null; 116 } else if (!isArrayOfStrings(obj)) { 117 throw getRuntime().newTypeError("backtrace must be Array of String"); 118 } else { 119 backtrace = (RubyArray) obj; 120 } 121 return backtrace(); 122 } 123 124 public RubyException exception(IRubyObject[] args) { 125 switch (args.length) { 126 case 0 : 127 return this; 128 case 1 : 129 if(args[0] == this) { 130 return this; 131 } 132 RubyException ret = (RubyException)rbClone(); 133 ret.initialize(args, Block.NULL_BLOCK); return ret; 135 default : 136 throw getRuntime().newArgumentError("Wrong argument count"); 137 } 138 } 139 140 public IRubyObject to_s() { 141 if (message.isNil()) { 142 return getRuntime().newString(getMetaClass().getName()); 143 } 144 message.setTaint(isTaint()); 145 return (RubyString) message.callMethod(getRuntime().getCurrentContext(), "to_s"); 146 } 147 148 152 public IRubyObject inspect() { 153 RubyModule rubyClass = getMetaClass(); 154 RubyString exception = RubyString.stringValue(this); 155 156 if (exception.getValue().length() == 0) { 157 return getRuntime().newString(rubyClass.getName()); 158 } 159 StringBuffer sb = new StringBuffer ("#<"); 160 sb.append(rubyClass.getName()).append(": ").append(exception.getValue()).append(">"); 161 return getRuntime().newString(sb.toString()); 162 } 163 164 public void printBacktrace(PrintStream errorStream) { 165 IRubyObject backtrace = callMethod(getRuntime().getCurrentContext(), "backtrace"); 166 if (!backtrace.isNil() && backtrace instanceof RubyArray) { 167 IRubyObject[] elements = ((RubyArray)backtrace.convertToArray()).toJavaArray(); 168 169 for (int i = 1; i < elements.length; i++) { 170 IRubyObject stackTraceLine = elements[i]; 171 if (stackTraceLine instanceof RubyString) { 172 printStackTraceLine(errorStream, stackTraceLine); 173 } 174 175 if (i == RubyException.TRACE_HEAD && elements.length > RubyException.TRACE_MAX) { 176 int hiddenLevels = elements.length - RubyException.TRACE_HEAD - RubyException.TRACE_TAIL; 177 errorStream.print("\t ... " + hiddenLevels + " levels...\n"); 178 i = elements.length - RubyException.TRACE_TAIL; 179 } 180 } 181 } 182 } 183 184 private void printStackTraceLine(PrintStream errorStream, IRubyObject stackTraceLine) { 185 errorStream.print("\tfrom " + stackTraceLine + '\n'); 186 } 187 188 private boolean isArrayOfStrings(IRubyObject backtrace) { 189 if (!(backtrace instanceof RubyArray)) return false; 190 191 IRubyObject[] elements = ((RubyArray) backtrace).toJavaArray(); 192 193 for (int i = 0 ; i < elements.length ; i++) { 194 if (!(elements[i] instanceof RubyString)) return false; 195 } 196 197 return true; 198 } 199 200 protected IRubyObject doClone() { 201 IRubyObject newObject = new RubyException(getRuntime(),getMetaClass().getRealClass()); 202 if (newObject.getType() != getMetaClass().getRealClass()) { 203 throw getRuntime().newTypeError("wrong instance allocation"); 204 } 205 return newObject; 206 } 207 } 208 | Popular Tags |