Skip to content

Commit fda488b

Browse files
committed
Implement Java exceptions for all primary Ruby exception types.
This includes everything in standard Ruby plus our additions but minus the errno classes. See #4781.
1 parent 415bef5 commit fda488b

79 files changed

Lines changed: 3288 additions & 176 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core/src/main/java/org/jruby/AbstractRubyException.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ public String getMessageAsJavaString() {
351351
return msg.isNil() ? null : msg.toString();
352352
}
353353

354-
private BacktraceData backtraceData;
354+
protected BacktraceData backtraceData;
355355
private IRubyObject backtrace;
356356
IRubyObject message;
357357
IRubyObject cause;

core/src/main/java/org/jruby/NativeException.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,16 @@
3030
import java.lang.reflect.Member;
3131
import org.jruby.anno.JRubyClass;
3232
import org.jruby.anno.JRubyMethod;
33+
import org.jruby.exceptions.RaiseException;
3334
import org.jruby.javasupport.Java;
3435
import org.jruby.runtime.Block;
3536
import org.jruby.runtime.ObjectAllocator;
37+
import org.jruby.runtime.ThreadContext;
38+
import org.jruby.runtime.backtrace.RubyStackTraceElement;
39+
import org.jruby.runtime.backtrace.TraceType;
3640
import org.jruby.runtime.builtin.IRubyObject;
3741

42+
@Deprecated
3843
@JRubyClass(name = "NativeException", parent = "RuntimeError")
3944
public class NativeException extends RubyException {
4045

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

66-
private static ObjectAllocator NATIVE_EXCEPTION_ALLOCATOR = new ObjectAllocator() {
67-
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
68-
NativeException instance = new NativeException(runtime, klazz);
69-
instance.setMetaClass(klazz);
70-
return instance;
71-
}
72-
};
71+
private static ObjectAllocator NATIVE_EXCEPTION_ALLOCATOR = (runtime, klazz) -> new NativeException(runtime, klazz);
7372

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

82+
@Override
83+
public void prepareBacktrace(ThreadContext context) {
84+
// if it's null, use cause's trace to build a raw stack trace
85+
if (backtraceData == null) {
86+
backtraceData = TraceType.Gather.RAW.getBacktraceData(getRuntime().getCurrentContext(), cause.getStackTrace());
87+
}
88+
}
89+
8390
@JRubyMethod
8491
public final IRubyObject cause() {
8592
return Java.getInstance(getRuntime(), getCause());

core/src/main/java/org/jruby/Ruby.java

Lines changed: 60 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@
200200
import java.util.concurrent.TimeUnit;
201201
import java.util.concurrent.atomic.AtomicInteger;
202202
import java.util.concurrent.atomic.AtomicLong;
203+
import java.util.function.Consumer;
203204
import java.util.regex.Pattern;
204205

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

15651566
private void initExceptions() {
1566-
standardError = RubyStandardError.createStandardErrorClass(this, exceptionClass);
1567-
runtimeError = defineClassIfAllowed("RuntimeError", standardError);
1568-
ioError = defineClassIfAllowed("IOError", standardError);
1569-
scriptError = defineClassIfAllowed("ScriptError", exceptionClass);
1570-
rangeError = defineClassIfAllowed("RangeError", standardError);
1567+
ifAllowed("StandardError", (ruby) -> standardError = RubyStandardError.define(ruby, exceptionClass));
1568+
ifAllowed("RubyError", (ruby) -> runtimeError = RubyRuntimeError.define(ruby, standardError));
1569+
ifAllowed("IOError", (ruby) -> ioError = RubyIOError.define(ruby, standardError));
1570+
ifAllowed("ScriptError", (ruby) -> scriptError = RubyScriptError.define(ruby, exceptionClass));
1571+
ifAllowed("RangeError", (ruby) -> rangeError = RubyRangeError.define(ruby, standardError));
1572+
ifAllowed("SignalException", (ruby) -> signalException = RubySignalException.define(ruby, exceptionClass));
1573+
ifAllowed("NameError", (ruby) -> {
1574+
nameError = RubyNameError.define(ruby, standardError);
1575+
nameErrorMessage = RubyNameError.defineMessage(ruby, nameError);
1576+
});
1577+
ifAllowed("NoMethodError", (ruby) -> noMethodError = RubyNoMethodError.define(ruby, nameError));
1578+
ifAllowed("SystemExit", (ruby) -> systemExit = RubySystemExit.define(ruby, exceptionClass));
1579+
ifAllowed("LocalJumpError", (ruby) -> localJumpError = RubyLocalJumpError.define(ruby, standardError));
1580+
ifAllowed("SystemCallError", (ruby) -> systemCallError = RubySystemCallError.define(ruby, standardError));
1581+
1582+
ifAllowed("Fatal", (ruby) -> fatal = RubyFatal.define(ruby, exceptionClass));
1583+
ifAllowed("Interrupt", (ruby) -> interrupt = RubyInterrupt.define(ruby, signalException));
1584+
ifAllowed("TypeError", (ruby) -> typeError = RubyTypeError.define(ruby, standardError));
1585+
ifAllowed("ArgumentError", (ruby) -> argumentError = RubyArgumentError.define(ruby, standardError));
1586+
ifAllowed("UncaughtThrowError", (ruby) -> uncaughtThrowError = RubyUncaughtThrowError.define(ruby, argumentError));
1587+
ifAllowed("IndexError", (ruby) -> indexError = RubyIndexError.define(ruby, standardError));
1588+
ifAllowed("StopIteration", (ruby) -> stopIteration = RubyStopIteration.define(ruby, indexError));
1589+
ifAllowed("SyntaxError", (ruby) -> syntaxError = RubySyntaxError.define(ruby, scriptError));
1590+
ifAllowed("LoadError", (ruby) -> loadError = RubyLoadError.define(ruby, scriptError));
1591+
ifAllowed("NotImplementedError", (ruby) -> notImplementedError = RubyNotImplementedError.define(ruby, scriptError));
1592+
ifAllowed("SecurityError", (ruby) -> securityError = RubySecurityError.define(ruby, exceptionClass));
1593+
ifAllowed("NoMemoryError", (ruby) -> noMemoryError = RubyNoMemoryError.define(ruby, exceptionClass));
1594+
ifAllowed("RegexpError", (ruby) -> regexpError = RubyRegexpError.define(ruby, standardError));
1595+
// Proposal to RubyCommons for interrupting Regexps
1596+
ifAllowed("InterruptedRegexpError", (ruby) -> interruptedRegexpError = RubyInterruptedRegexpError.define(ruby, regexpError));
1597+
ifAllowed("EOFError", (ruby) -> eofError = RubyEOFError.define(ruby, ioError));
1598+
ifAllowed("ThreadError", (ruby) -> threadError = RubyThreadError.define(ruby, standardError));
1599+
ifAllowed("ConcurrencyError", (ruby) -> concurrencyError = RubyConcurrencyError.define(ruby, threadError));
1600+
ifAllowed("SystemStackError", (ruby) -> systemStackError = RubySystemStackError.define(ruby, exceptionClass));
1601+
ifAllowed("ZeroDivisionError", (ruby) -> zeroDivisionError = RubyZeroDivisionError.define(ruby, standardError));
1602+
ifAllowed("FloatDomainError", (ruby) -> RubyFloatDomainError.define(ruby, rangeError));
1603+
ifAllowed("EncodingError", (ruby) -> {
1604+
encodingError = RubyEncodingError.define(ruby, standardError);
1605+
encodingCompatibilityError = RubyEncodingError.RubyCompatibilityError.define(ruby, encodingError, encodingClass);
1606+
invalidByteSequenceError = RubyEncodingError.RubyInvalidByteSequenceError.define(ruby, encodingError, encodingClass);
1607+
undefinedConversionError = RubyEncodingError.RubyUndefinedConversionError.define(ruby, encodingError, encodingClass);
1608+
converterNotFoundError = RubyEncodingError.RubyConverterNotFoundError.define(ruby, encodingError, encodingClass);
1609+
});
1610+
ifAllowed("Fiber", (ruby) -> fiberError = RubyFiberError.define(ruby, standardError));
1611+
ifAllowed("ConcurrencyError", (ruby) -> concurrencyError = RubyConcurrencyError.define(ruby, threadError));
1612+
ifAllowed("KeyError", (ruby) -> keyError = RubyKeyError.define(ruby, indexError));
1613+
ifAllowed("DomainError", (ruby) -> mathDomainError = RubyDomainError.define(ruby, argumentError, mathModule));
15711614

1572-
if (profile.allowClass("SignalException")) {
1573-
signalException = RubySignalException.createSignalExceptionClass(this, exceptionClass);
1574-
}
1575-
if (profile.allowClass("NameError")) {
1576-
nameError = RubyNameError.createNameErrorClass(this, standardError);
1577-
nameErrorMessage = RubyNameError.createNameErrorMessageClass(this, nameError);
1578-
}
1579-
if (profile.allowClass("NoMethodError")) {
1580-
noMethodError = RubyNoMethodError.createNoMethodErrorClass(this, nameError);
1581-
}
1582-
if (profile.allowClass("SystemExit")) {
1583-
systemExit = RubySystemExit.createSystemExitClass(this, exceptionClass);
1584-
}
1585-
if (profile.allowClass("LocalJumpError")) {
1586-
localJumpError = RubyLocalJumpError.createLocalJumpErrorClass(this, standardError);
1615+
initErrno();
1616+
1617+
initNativeException();
1618+
}
1619+
1620+
private void ifAllowed(String name, Consumer<Ruby> callback) {
1621+
if (profile.allowClass(name)) {
1622+
callback.accept(this);
15871623
}
1624+
}
1625+
1626+
@SuppressWarnings("deprecation")
1627+
private void initNativeException() {
15881628
if (profile.allowClass("NativeException")) {
15891629
nativeException = NativeException.createClass(this, runtimeError);
15901630
}
1591-
if (profile.allowClass("SystemCallError")) {
1592-
systemCallError = RubySystemCallError.createSystemCallErrorClass(this, standardError);
1593-
}
1594-
1595-
fatal = defineClassIfAllowed("Fatal", exceptionClass);
1596-
if (profile.allowClass("Interrupt")) {
1597-
interrupt = RubyInterrupt.createInterruptClass(this, signalException);
1598-
}
1599-
typeError = defineClassIfAllowed("TypeError", standardError);
1600-
argumentError = defineClassIfAllowed("ArgumentError", standardError);
1601-
if (profile.allowClass("UncaughtThrowError")) {
1602-
uncaughtThrowError = RubyUncaughtThrowError.createUncaughtThrowErrorClass(this, argumentError);
1603-
}
1604-
indexError = defineClassIfAllowed("IndexError", standardError);
1605-
if (profile.allowClass("StopIteration")) {
1606-
stopIteration = RubyStopIteration.createStopIterationClass(this, indexError);
1607-
}
1608-
syntaxError = defineClassIfAllowed("SyntaxError", scriptError);
1609-
loadError = defineClassIfAllowed("LoadError", scriptError);
1610-
notImplementedError = defineClassIfAllowed("NotImplementedError", scriptError);
1611-
securityError = defineClassIfAllowed("SecurityError", exceptionClass);
1612-
noMemoryError = defineClassIfAllowed("NoMemoryError", exceptionClass);
1613-
regexpError = defineClassIfAllowed("RegexpError", standardError);
1614-
interruptedRegexpError = defineClassIfAllowed("InterruptedRegexpError", regexpError); // Proposal to RubyCommons for interrupting Regexps
1615-
eofError = defineClassIfAllowed("EOFError", ioError);
1616-
threadError = defineClassIfAllowed("ThreadError", standardError);
1617-
concurrencyError = defineClassIfAllowed("ConcurrencyError", threadError);
1618-
systemStackError = defineClassIfAllowed("SystemStackError", exceptionClass);
1619-
zeroDivisionError = defineClassIfAllowed("ZeroDivisionError", standardError);
1620-
floatDomainError = defineClassIfAllowed("FloatDomainError", rangeError);
1621-
1622-
if (profile.allowClass("EncodingError")) {
1623-
encodingError = defineClass("EncodingError", standardError, standardError.getAllocator());
1624-
encodingCompatibilityError = defineClassUnder("CompatibilityError", encodingError, encodingError.getAllocator(), encodingClass);
1625-
invalidByteSequenceError = defineClassUnder("InvalidByteSequenceError", encodingError, encodingError.getAllocator(), encodingClass);
1626-
invalidByteSequenceError.defineAnnotatedMethods(RubyConverter.EncodingErrorMethods.class);
1627-
invalidByteSequenceError.defineAnnotatedMethods(RubyConverter.InvalidByteSequenceErrorMethods.class);
1628-
undefinedConversionError = defineClassUnder("UndefinedConversionError", encodingError, encodingError.getAllocator(), encodingClass);
1629-
undefinedConversionError.defineAnnotatedMethods(RubyConverter.EncodingErrorMethods.class);
1630-
undefinedConversionError.defineAnnotatedMethods(RubyConverter.UndefinedConversionErrorMethods.class);
1631-
converterNotFoundError = defineClassUnder("ConverterNotFoundError", encodingError, encodingError.getAllocator(), encodingClass);
1632-
fiberError = defineClass("FiberError", standardError, standardError.getAllocator());
1633-
}
1634-
concurrencyError = defineClassIfAllowed("ConcurrencyError", threadError);
1635-
keyError = defineClassIfAllowed("KeyError", indexError);
1636-
1637-
mathDomainError = defineClassUnder("DomainError", argumentError, argumentError.getAllocator(), mathModule);
1638-
1639-
initErrno();
16401631
}
16411632

16421633
private void initLibraries() {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/***** BEGIN LICENSE BLOCK *****
2+
* Version: EPL 2.0/GPL 2.0/LGPL 2.1
3+
*
4+
* The contents of this file are subject to the Eclipse Public
5+
* License Version 2.0 (the "License"); you may not use this file
6+
* except in compliance with the License. You may obtain a copy of
7+
* the License at http://www.eclipse.org/legal/epl-v10.html
8+
*
9+
* Software distributed under the License is distributed on an "AS
10+
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11+
* implied. See the License for the specific language governing
12+
* rights and limitations under the License.
13+
*
14+
* Alternatively, the contents of this file may be used under the terms of
15+
* either of the GNU General Public License Version 2 or later (the "GPL"),
16+
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
17+
* in which case the provisions of the GPL or the LGPL are applicable instead
18+
* of those above. If you wish to allow use of your version of this file only
19+
* under the terms of either the GPL or the LGPL, and not to allow others to
20+
* use your version of this file under the terms of the EPL, indicate your
21+
* decision by deleting the provisions above and replace them with the notice
22+
* and other provisions required by the GPL or the LGPL. If you do not delete
23+
* the provisions above, a recipient may use your version of this file under
24+
* the terms of any one of the EPL, the GPL or the LGPL.
25+
***** END LICENSE BLOCK *****/
26+
27+
package org.jruby;
28+
29+
import org.jruby.anno.JRubyClass;
30+
import org.jruby.exceptions.RaiseException;
31+
import org.jruby.exceptions.ArgumentError;
32+
33+
/**
34+
* The Java representation of a Ruby ArgumentError.
35+
*
36+
* @see ArgumentError
37+
*/
38+
@JRubyClass(name="ArgumentError", parent="StandardError")
39+
public class RubyArgumentError extends RubyStandardError {
40+
protected RubyArgumentError(Ruby runtime, RubyClass exceptionClass) {
41+
super(runtime, exceptionClass);
42+
}
43+
44+
protected RubyArgumentError(Ruby runtime, RubyClass exceptionClass, String message) {
45+
super(runtime, exceptionClass, message);
46+
}
47+
48+
static RubyClass define(Ruby runtime, RubyClass exceptionClass) {
49+
RubyClass argumentErrorClass = runtime.defineClass("ArgumentError", exceptionClass, (r, klass) -> new RubyArgumentError(runtime, klass));
50+
51+
return argumentErrorClass;
52+
}
53+
54+
@Override
55+
protected RaiseException constructRaiseException(String message) {
56+
return new ArgumentError(message, this);
57+
}
58+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/***** BEGIN LICENSE BLOCK *****
2+
* Version: EPL 2.0/GPL 2.0/LGPL 2.1
3+
*
4+
* The contents of this file are subject to the Eclipse Public
5+
* License Version 2.0 (the "License"); you may not use this file
6+
* except in compliance with the License. You may obtain a copy of
7+
* the License at http://www.eclipse.org/legal/epl-v10.html
8+
*
9+
* Software distributed under the License is distributed on an "AS
10+
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11+
* implied. See the License for the specific language governing
12+
* rights and limitations under the License.
13+
*
14+
* Alternatively, the contents of this file may be used under the terms of
15+
* either of the GNU General Public License Version 2 or later (the "GPL"),
16+
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
17+
* in which case the provisions of the GPL or the LGPL are applicable instead
18+
* of those above. If you wish to allow use of your version of this file only
19+
* under the terms of either the GPL or the LGPL, and not to allow others to
20+
* use your version of this file under the terms of the EPL, indicate your
21+
* decision by deleting the provisions above and replace them with the notice
22+
* and other provisions required by the GPL or the LGPL. If you do not delete
23+
* the provisions above, a recipient may use your version of this file under
24+
* the terms of any one of the EPL, the GPL or the LGPL.
25+
***** END LICENSE BLOCK *****/
26+
27+
package org.jruby;
28+
29+
import org.jruby.anno.JRubyClass;
30+
import org.jruby.exceptions.RaiseException;
31+
import org.jruby.exceptions.ConcurrencyError;
32+
33+
/**
34+
* The Java representation of a Ruby ConcurrencyError.
35+
*
36+
* @see ConcurrencyError
37+
*/
38+
@JRubyClass(name="ConcurrencyError", parent="ThreadError")
39+
public class RubyConcurrencyError extends RubyThreadError {
40+
protected RubyConcurrencyError(Ruby runtime, RubyClass exceptionClass) {
41+
super(runtime, exceptionClass);
42+
}
43+
44+
static RubyClass define(Ruby runtime, RubyClass exceptionClass) {
45+
RubyClass concurrencyErrorClass = runtime.defineClass("ConcurrencyError", exceptionClass, (r, klass) -> new RubyConcurrencyError(runtime, klass));
46+
47+
return concurrencyErrorClass;
48+
}
49+
50+
protected RaiseException constructRaiseException(String message) {
51+
return new ConcurrencyError(message, this);
52+
}
53+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/***** BEGIN LICENSE BLOCK *****
2+
* Version: EPL 2.0/GPL 2.0/LGPL 2.1
3+
*
4+
* The contents of this file are subject to the Eclipse Public
5+
* License Version 2.0 (the "License"); you may not use this file
6+
* except in compliance with the License. You may obtain a copy of
7+
* the License at http://www.eclipse.org/legal/epl-v10.html
8+
*
9+
* Software distributed under the License is distributed on an "AS
10+
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11+
* implied. See the License for the specific language governing
12+
* rights and limitations under the License.
13+
*
14+
* Alternatively, the contents of this file may be used under the terms of
15+
* either of the GNU General Public License Version 2 or later (the "GPL"),
16+
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
17+
* in which case the provisions of the GPL or the LGPL are applicable instead
18+
* of those above. If you wish to allow use of your version of this file only
19+
* under the terms of either the GPL or the LGPL, and not to allow others to
20+
* use your version of this file under the terms of the EPL, indicate your
21+
* decision by deleting the provisions above and replace them with the notice
22+
* and other provisions required by the GPL or the LGPL. If you do not delete
23+
* the provisions above, a recipient may use your version of this file under
24+
* the terms of any one of the EPL, the GPL or the LGPL.
25+
***** END LICENSE BLOCK *****/
26+
27+
package org.jruby;
28+
29+
import org.jruby.anno.JRubyClass;
30+
import org.jruby.exceptions.DomainError;
31+
import org.jruby.exceptions.RaiseException;
32+
33+
/**
34+
/**
35+
* The Java representation of a Ruby DomainError.
36+
*
37+
* @see DomainError
38+
* @see RubyEnumerator
39+
* @author kares
40+
*/
41+
@JRubyClass(name="DomainError", parent="ArgumentError")
42+
public class RubyDomainError extends RubyArgumentError {
43+
protected RubyDomainError(Ruby runtime, RubyClass exceptionClass) {
44+
super(runtime, exceptionClass);
45+
}
46+
47+
static RubyClass define(Ruby runtime, RubyClass superClass, RubyModule under) {
48+
return under.defineClassUnder("DomainError", superClass, (runtime1, klass) -> new RubyDomainError(runtime1, klass));
49+
}
50+
51+
@Override
52+
protected RaiseException constructRaiseException(String message) {
53+
return new DomainError(message, this);
54+
}
55+
56+
}

0 commit comments

Comments
 (0)