package org.jruby.exceptions;
import java.lang.reflect.Member;
import java.util.Arrays;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyClass;
import org.jruby.RubyException;
import org.jruby.RubyInstanceConfig;
import org.jruby.RubyStandardError;
import org.jruby.RubyString;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.JavaSites;
import org.jruby.runtime.RubyEvent;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.backtrace.RubyStackTraceElement;
import org.jruby.runtime.backtrace.TraceType;
import org.jruby.runtime.builtin.IRubyObject;
public class RaiseException extends JumpException {
private static final long serialVersionUID = -7612079169559973951L;
private RubyException exception;
private String providedMessage;
protected RaiseException(String message, RubyException exception) {
super(message);
setException(exception);
preRaise(exception.getRuntime().getCurrentContext(), RubyException.retrieveBacktrace(exception), true);
}
@Override
public final Throwable fillInStackTrace() {
return this;
}
@Deprecated
public static RaiseException from(RubyException exception, IRubyObject backtrace) {
return new RaiseException(exception, backtrace);
}
public static RaiseException from(Ruby runtime, RubyClass exceptionClass, String msg) {
return RubyException.newException(runtime, exceptionClass, msg).toThrowable();
}
public static RaiseException from(Ruby runtime, RubyClass exceptionClass, String msg, IRubyObject backtrace) {
RubyException exception = RubyException.newException(runtime, exceptionClass, msg);
exception.setBacktrace(backtrace);
return exception.toThrowable();
}
@Override
public String getMessage() {
if (providedMessage == null) {
providedMessage = '(' + exception.getMetaClass().getBaseName() + ") " + exception.message(exception.getRuntime().getCurrentContext()).asJavaString();
}
return providedMessage;
}
public final RubyException getException() {
return exception;
}
private void preRaise(ThreadContext context, IRubyObject backtrace, boolean capture) {
context.runtime.incrementExceptionCount();
if (RubyInstanceConfig.LOG_EXCEPTIONS) TraceType.logException(exception);
doSetLastError(context);
doCallEventHook(context);
if (backtrace == null) {
if (capture) {
if (requiresBacktrace(context)) exception.captureBacktrace(context);
setStackTraceFromException();
}
} else {
exception.setBacktrace(backtrace);
if (!backtrace.isNil() && !isEmptyArray(backtrace)) {
if (requiresBacktrace(context)) exception.captureBacktrace(context);
}
setStackTraceFromException();
}
}
private void setStackTraceFromException() {
RubyStackTraceElement[] rubyTrace = exception.getBacktraceElements();
if (rubyTrace.length > 5 && "getStackTrace".equals(rubyTrace[0].getMethodName())) {
int skip = 0;
if ("preRaise".equals(rubyTrace[4].getMethodName())) {
skip = 5;
} else if ("preRaise".equals(rubyTrace[3].getMethodName())) {
skip = 4;
}
rubyTrace = Arrays.copyOfRange(rubyTrace, skip, rubyTrace.length);
}
setStackTrace(javaTraceFromRubyTrace(rubyTrace));
}
private StackTraceElement[] skipFillInStackTracePart(StackTraceElement[] trace) {
final int len = trace.length;
if (len >= 3) {
int skip = 0;
if ("preRaise".equals(trace[2].getMethodName())) {
skip = 3;
} else if ("preRaise".equals(trace[1].getMethodName())) {
skip = 2;
} else if ("fillInStackTrace".equals(trace[2].getMethodName())) {
skip = 3;
}
return Arrays.copyOfRange(trace, skip, len);
}
return trace;
}
private void fillInStackTraceSkipPreRaise() {
originalFillInStackTrace();
StackTraceElement[] curTrace = getStackTrace();
StackTraceElement[] newTrace = skipFillInStackTracePart(curTrace);
if (newTrace != curTrace) setStackTrace(newTrace);
}
private static void doCallEventHook(final ThreadContext context) {
if (context.runtime.hasEventHooks()) {
context.runtime.callEventHooks(context, RubyEvent.RAISE, context.getFile(), context.getLine(), context.getFrameName(), context.getFrameKlazz());
}
}
private void doSetLastError(final ThreadContext context) {
context.setErrorInfo(exception);
}
protected final void setException(RubyException newException) {
this.exception = newException;
}
public static StackTraceElement[] javaTraceFromRubyTrace(RubyStackTraceElement[] trace) {
StackTraceElement[] newTrace = new StackTraceElement[trace.length];
for (int i = 0; i < newTrace.length; i++) {
newTrace[i] = trace[i].asStackTraceElement();
}
return newTrace;
}
@Deprecated
public static RaiseException createNativeRaiseException(Ruby runtime, Throwable cause) {
return createNativeRaiseException(runtime, cause, null);
}
@Deprecated
public static RaiseException createNativeRaiseException(Ruby runtime, Throwable cause, Member target) {
org.jruby.NativeException nativeException = new org.jruby.NativeException(runtime, runtime.getNativeException(), cause);
return new RaiseException(cause, nativeException);
}
@Deprecated
public RaiseException(Throwable cause, org.jruby.NativeException nativeException) {
super(nativeException.getMessageAsJavaString(), cause);
providedMessage = super.getMessage();
setException(nativeException);
preRaise(nativeException.getRuntime().getCurrentContext(), nativeException.getCause().getStackTrace());
setStackTraceFromException();
}
@Deprecated
public RaiseException(RubyException exception) {
this(exception.getMessageAsJavaString(), exception);
}
@Deprecated
public RaiseException(RubyException exception, boolean unused) {
this(exception.getMessageAsJavaString(), exception);
}
@Deprecated
public RaiseException(RubyException exception, IRubyObject backtrace) {
super(exception.getMessageAsJavaString());
setException(exception);
preRaise(exception.getRuntime().getCurrentContext(), backtrace, true);
}
@Deprecated
public RaiseException(Ruby runtime, RubyClass exceptionClass, String msg) {
this(runtime, exceptionClass, msg, null);
}
@Deprecated
public RaiseException(Ruby runtime, RubyClass exceptionClass, String msg, boolean unused) {
this(runtime, exceptionClass, msg, null);
}
@Deprecated
public RaiseException(Ruby runtime, RubyClass exceptionClass, String msg, IRubyObject backtrace) {
super(msg == null ? msg = "No message available" : msg);
providedMessage = '(' + exceptionClass.getName() + ") " + msg;
final ThreadContext context = runtime.getCurrentContext();
setException(RubyException.newException(context, exceptionClass, RubyString.newUnicodeString(runtime, msg)));
preRaise(context, backtrace, true);
}
@Deprecated
public RaiseException(Ruby runtime, RubyClass exceptionClass, String msg, IRubyObject backtrace, boolean unused) {
this(runtime, exceptionClass, msg, backtrace);
}
@Deprecated
protected final void setException(RubyException newException, boolean unused) {
this.exception = newException;
}
@Deprecated
private void preRaise(ThreadContext context, StackTraceElement[] javaTrace) {
preRaise(context, null, false);
if (requiresBacktrace(context)) {
exception.prepareIntegratedBacktrace(context, javaTrace);
setStackTraceFromException();
} else {
setStackTrace(skipFillInStackTracePart(javaTrace));
}
}
private boolean requiresBacktrace(ThreadContext context) {
return context.exceptionRequiresBacktrace || !(exception instanceof RubyStandardError);
}
private static boolean isEmptyArray(final IRubyObject ary) {
return ary instanceof RubyArray && ((RubyArray) ary).size() == 0;
}
private static JavaSites.RaiseExceptionSites sites(ThreadContext context) {
return context.sites.RaiseException;
}
}