Skip to content

Commit

Permalink
Implement Java exceptions for all primary Ruby exception types.
Browse files Browse the repository at this point in the history
This includes everything in standard Ruby plus our additions but
minus the errno classes.

See #4781.
headius committed Feb 28, 2018
1 parent 415bef5 commit fda488b
Showing 79 changed files with 3,288 additions and 176 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/AbstractRubyException.java
Original file line number Diff line number Diff line change
@@ -351,7 +351,7 @@ public String getMessageAsJavaString() {
return msg.isNil() ? null : msg.toString();
}

private BacktraceData backtraceData;
protected BacktraceData backtraceData;
private IRubyObject backtrace;
IRubyObject message;
IRubyObject cause;
21 changes: 14 additions & 7 deletions core/src/main/java/org/jruby/NativeException.java
Original file line number Diff line number Diff line change
@@ -30,11 +30,16 @@
import java.lang.reflect.Member;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.exceptions.RaiseException;
import org.jruby.javasupport.Java;
import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.backtrace.RubyStackTraceElement;
import org.jruby.runtime.backtrace.TraceType;
import org.jruby.runtime.builtin.IRubyObject;

@Deprecated
@JRubyClass(name = "NativeException", parent = "RuntimeError")
public class NativeException extends RubyException {

@@ -63,13 +68,7 @@ private NativeException(Ruby runtime, RubyClass rubyClass) {
this.messageAsJavaString = null;
}

private static ObjectAllocator NATIVE_EXCEPTION_ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
NativeException instance = new NativeException(runtime, klazz);
instance.setMetaClass(klazz);
return instance;
}
};
private static ObjectAllocator NATIVE_EXCEPTION_ALLOCATOR = (runtime, klazz) -> new NativeException(runtime, klazz);

public static RubyClass createClass(Ruby runtime, RubyClass baseClass) {
RubyClass exceptionClass = runtime.defineClass(CLASS_NAME, baseClass, NATIVE_EXCEPTION_ALLOCATOR);
@@ -80,6 +79,14 @@ public static RubyClass createClass(Ruby runtime, RubyClass baseClass) {
return exceptionClass;
}

@Override
public void prepareBacktrace(ThreadContext context) {
// if it's null, use cause's trace to build a raw stack trace
if (backtraceData == null) {
backtraceData = TraceType.Gather.RAW.getBacktraceData(getRuntime().getCurrentContext(), cause.getStackTrace());
}
}

@JRubyMethod
public final IRubyObject cause() {
return Java.getInstance(getRuntime(), getCause());
129 changes: 60 additions & 69 deletions core/src/main/java/org/jruby/Ruby.java
Original file line number Diff line number Diff line change
@@ -200,6 +200,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.regex.Pattern;

import static java.lang.invoke.MethodHandles.explicitCastArguments;
@@ -1563,80 +1564,70 @@ public IRubyObject[] getNilPrefilledArray() {
}

private void initExceptions() {
standardError = RubyStandardError.createStandardErrorClass(this, exceptionClass);
runtimeError = defineClassIfAllowed("RuntimeError", standardError);
ioError = defineClassIfAllowed("IOError", standardError);
scriptError = defineClassIfAllowed("ScriptError", exceptionClass);
rangeError = defineClassIfAllowed("RangeError", standardError);
ifAllowed("StandardError", (ruby) -> standardError = RubyStandardError.define(ruby, exceptionClass));
ifAllowed("RubyError", (ruby) -> runtimeError = RubyRuntimeError.define(ruby, standardError));
ifAllowed("IOError", (ruby) -> ioError = RubyIOError.define(ruby, standardError));
ifAllowed("ScriptError", (ruby) -> scriptError = RubyScriptError.define(ruby, exceptionClass));
ifAllowed("RangeError", (ruby) -> rangeError = RubyRangeError.define(ruby, standardError));
ifAllowed("SignalException", (ruby) -> signalException = RubySignalException.define(ruby, exceptionClass));
ifAllowed("NameError", (ruby) -> {
nameError = RubyNameError.define(ruby, standardError);
nameErrorMessage = RubyNameError.defineMessage(ruby, nameError);
});
ifAllowed("NoMethodError", (ruby) -> noMethodError = RubyNoMethodError.define(ruby, nameError));
ifAllowed("SystemExit", (ruby) -> systemExit = RubySystemExit.define(ruby, exceptionClass));
ifAllowed("LocalJumpError", (ruby) -> localJumpError = RubyLocalJumpError.define(ruby, standardError));
ifAllowed("SystemCallError", (ruby) -> systemCallError = RubySystemCallError.define(ruby, standardError));

ifAllowed("Fatal", (ruby) -> fatal = RubyFatal.define(ruby, exceptionClass));
ifAllowed("Interrupt", (ruby) -> interrupt = RubyInterrupt.define(ruby, signalException));
ifAllowed("TypeError", (ruby) -> typeError = RubyTypeError.define(ruby, standardError));
ifAllowed("ArgumentError", (ruby) -> argumentError = RubyArgumentError.define(ruby, standardError));
ifAllowed("UncaughtThrowError", (ruby) -> uncaughtThrowError = RubyUncaughtThrowError.define(ruby, argumentError));
ifAllowed("IndexError", (ruby) -> indexError = RubyIndexError.define(ruby, standardError));
ifAllowed("StopIteration", (ruby) -> stopIteration = RubyStopIteration.define(ruby, indexError));
ifAllowed("SyntaxError", (ruby) -> syntaxError = RubySyntaxError.define(ruby, scriptError));
ifAllowed("LoadError", (ruby) -> loadError = RubyLoadError.define(ruby, scriptError));
ifAllowed("NotImplementedError", (ruby) -> notImplementedError = RubyNotImplementedError.define(ruby, scriptError));
ifAllowed("SecurityError", (ruby) -> securityError = RubySecurityError.define(ruby, exceptionClass));
ifAllowed("NoMemoryError", (ruby) -> noMemoryError = RubyNoMemoryError.define(ruby, exceptionClass));
ifAllowed("RegexpError", (ruby) -> regexpError = RubyRegexpError.define(ruby, standardError));
// Proposal to RubyCommons for interrupting Regexps
ifAllowed("InterruptedRegexpError", (ruby) -> interruptedRegexpError = RubyInterruptedRegexpError.define(ruby, regexpError));
ifAllowed("EOFError", (ruby) -> eofError = RubyEOFError.define(ruby, ioError));
ifAllowed("ThreadError", (ruby) -> threadError = RubyThreadError.define(ruby, standardError));
ifAllowed("ConcurrencyError", (ruby) -> concurrencyError = RubyConcurrencyError.define(ruby, threadError));
ifAllowed("SystemStackError", (ruby) -> systemStackError = RubySystemStackError.define(ruby, exceptionClass));
ifAllowed("ZeroDivisionError", (ruby) -> zeroDivisionError = RubyZeroDivisionError.define(ruby, standardError));
ifAllowed("FloatDomainError", (ruby) -> RubyFloatDomainError.define(ruby, rangeError));
ifAllowed("EncodingError", (ruby) -> {
encodingError = RubyEncodingError.define(ruby, standardError);
encodingCompatibilityError = RubyEncodingError.RubyCompatibilityError.define(ruby, encodingError, encodingClass);
invalidByteSequenceError = RubyEncodingError.RubyInvalidByteSequenceError.define(ruby, encodingError, encodingClass);
undefinedConversionError = RubyEncodingError.RubyUndefinedConversionError.define(ruby, encodingError, encodingClass);
converterNotFoundError = RubyEncodingError.RubyConverterNotFoundError.define(ruby, encodingError, encodingClass);
});
ifAllowed("Fiber", (ruby) -> fiberError = RubyFiberError.define(ruby, standardError));
ifAllowed("ConcurrencyError", (ruby) -> concurrencyError = RubyConcurrencyError.define(ruby, threadError));
ifAllowed("KeyError", (ruby) -> keyError = RubyKeyError.define(ruby, indexError));
ifAllowed("DomainError", (ruby) -> mathDomainError = RubyDomainError.define(ruby, argumentError, mathModule));

if (profile.allowClass("SignalException")) {
signalException = RubySignalException.createSignalExceptionClass(this, exceptionClass);
}
if (profile.allowClass("NameError")) {
nameError = RubyNameError.createNameErrorClass(this, standardError);
nameErrorMessage = RubyNameError.createNameErrorMessageClass(this, nameError);
}
if (profile.allowClass("NoMethodError")) {
noMethodError = RubyNoMethodError.createNoMethodErrorClass(this, nameError);
}
if (profile.allowClass("SystemExit")) {
systemExit = RubySystemExit.createSystemExitClass(this, exceptionClass);
}
if (profile.allowClass("LocalJumpError")) {
localJumpError = RubyLocalJumpError.createLocalJumpErrorClass(this, standardError);
initErrno();

initNativeException();
}

private void ifAllowed(String name, Consumer<Ruby> callback) {
if (profile.allowClass(name)) {
callback.accept(this);
}
}

@SuppressWarnings("deprecation")
private void initNativeException() {
if (profile.allowClass("NativeException")) {
nativeException = NativeException.createClass(this, runtimeError);
}
if (profile.allowClass("SystemCallError")) {
systemCallError = RubySystemCallError.createSystemCallErrorClass(this, standardError);
}

fatal = defineClassIfAllowed("Fatal", exceptionClass);
if (profile.allowClass("Interrupt")) {
interrupt = RubyInterrupt.createInterruptClass(this, signalException);
}
typeError = defineClassIfAllowed("TypeError", standardError);
argumentError = defineClassIfAllowed("ArgumentError", standardError);
if (profile.allowClass("UncaughtThrowError")) {
uncaughtThrowError = RubyUncaughtThrowError.createUncaughtThrowErrorClass(this, argumentError);
}
indexError = defineClassIfAllowed("IndexError", standardError);
if (profile.allowClass("StopIteration")) {
stopIteration = RubyStopIteration.createStopIterationClass(this, indexError);
}
syntaxError = defineClassIfAllowed("SyntaxError", scriptError);
loadError = defineClassIfAllowed("LoadError", scriptError);
notImplementedError = defineClassIfAllowed("NotImplementedError", scriptError);
securityError = defineClassIfAllowed("SecurityError", exceptionClass);
noMemoryError = defineClassIfAllowed("NoMemoryError", exceptionClass);
regexpError = defineClassIfAllowed("RegexpError", standardError);
interruptedRegexpError = defineClassIfAllowed("InterruptedRegexpError", regexpError); // Proposal to RubyCommons for interrupting Regexps
eofError = defineClassIfAllowed("EOFError", ioError);
threadError = defineClassIfAllowed("ThreadError", standardError);
concurrencyError = defineClassIfAllowed("ConcurrencyError", threadError);
systemStackError = defineClassIfAllowed("SystemStackError", exceptionClass);
zeroDivisionError = defineClassIfAllowed("ZeroDivisionError", standardError);
floatDomainError = defineClassIfAllowed("FloatDomainError", rangeError);

if (profile.allowClass("EncodingError")) {
encodingError = defineClass("EncodingError", standardError, standardError.getAllocator());
encodingCompatibilityError = defineClassUnder("CompatibilityError", encodingError, encodingError.getAllocator(), encodingClass);
invalidByteSequenceError = defineClassUnder("InvalidByteSequenceError", encodingError, encodingError.getAllocator(), encodingClass);
invalidByteSequenceError.defineAnnotatedMethods(RubyConverter.EncodingErrorMethods.class);
invalidByteSequenceError.defineAnnotatedMethods(RubyConverter.InvalidByteSequenceErrorMethods.class);
undefinedConversionError = defineClassUnder("UndefinedConversionError", encodingError, encodingError.getAllocator(), encodingClass);
undefinedConversionError.defineAnnotatedMethods(RubyConverter.EncodingErrorMethods.class);
undefinedConversionError.defineAnnotatedMethods(RubyConverter.UndefinedConversionErrorMethods.class);
converterNotFoundError = defineClassUnder("ConverterNotFoundError", encodingError, encodingError.getAllocator(), encodingClass);
fiberError = defineClass("FiberError", standardError, standardError.getAllocator());
}
concurrencyError = defineClassIfAllowed("ConcurrencyError", threadError);
keyError = defineClassIfAllowed("KeyError", indexError);

mathDomainError = defineClassUnder("DomainError", argumentError, argumentError.getAllocator(), mathModule);

initErrno();
}

private void initLibraries() {
58 changes: 58 additions & 0 deletions core/src/main/java/org/jruby/RubyArgumentError.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 2.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/

package org.jruby;

import org.jruby.anno.JRubyClass;
import org.jruby.exceptions.RaiseException;
import org.jruby.exceptions.ArgumentError;

/**
* The Java representation of a Ruby ArgumentError.
*
* @see ArgumentError
*/
@JRubyClass(name="ArgumentError", parent="StandardError")
public class RubyArgumentError extends RubyStandardError {
protected RubyArgumentError(Ruby runtime, RubyClass exceptionClass) {
super(runtime, exceptionClass);
}

protected RubyArgumentError(Ruby runtime, RubyClass exceptionClass, String message) {
super(runtime, exceptionClass, message);
}

static RubyClass define(Ruby runtime, RubyClass exceptionClass) {
RubyClass argumentErrorClass = runtime.defineClass("ArgumentError", exceptionClass, (r, klass) -> new RubyArgumentError(runtime, klass));

return argumentErrorClass;
}

@Override
protected RaiseException constructRaiseException(String message) {
return new ArgumentError(message, this);
}
}
53 changes: 53 additions & 0 deletions core/src/main/java/org/jruby/RubyConcurrencyError.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 2.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/

package org.jruby;

import org.jruby.anno.JRubyClass;
import org.jruby.exceptions.RaiseException;
import org.jruby.exceptions.ConcurrencyError;

/**
* The Java representation of a Ruby ConcurrencyError.
*
* @see ConcurrencyError
*/
@JRubyClass(name="ConcurrencyError", parent="ThreadError")
public class RubyConcurrencyError extends RubyThreadError {
protected RubyConcurrencyError(Ruby runtime, RubyClass exceptionClass) {
super(runtime, exceptionClass);
}

static RubyClass define(Ruby runtime, RubyClass exceptionClass) {
RubyClass concurrencyErrorClass = runtime.defineClass("ConcurrencyError", exceptionClass, (r, klass) -> new RubyConcurrencyError(runtime, klass));

return concurrencyErrorClass;
}

protected RaiseException constructRaiseException(String message) {
return new ConcurrencyError(message, this);
}
}
56 changes: 56 additions & 0 deletions core/src/main/java/org/jruby/RubyDomainError.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 2.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/

package org.jruby;

import org.jruby.anno.JRubyClass;
import org.jruby.exceptions.DomainError;
import org.jruby.exceptions.RaiseException;

/**
/**
* The Java representation of a Ruby DomainError.
*
* @see DomainError
* @see RubyEnumerator
* @author kares
*/
@JRubyClass(name="DomainError", parent="ArgumentError")
public class RubyDomainError extends RubyArgumentError {
protected RubyDomainError(Ruby runtime, RubyClass exceptionClass) {
super(runtime, exceptionClass);
}

static RubyClass define(Ruby runtime, RubyClass superClass, RubyModule under) {
return under.defineClassUnder("DomainError", superClass, (runtime1, klass) -> new RubyDomainError(runtime1, klass));
}

@Override
protected RaiseException constructRaiseException(String message) {
return new DomainError(message, this);
}

}
Loading

0 comments on commit fda488b

Please sign in to comment.