Skip to content

Commit 3ed77b7

Browse files
committed
Refactor and share logic for Kernel#raise and Thread#raise
* Capture the backtrace directly in Kernel#raise and Thread#raise to avoid capturing more backtrace entries to only skip them later. * Match MRI for the message printed with $DEBUG true for raised exceptions.
1 parent b0e151f commit 3ed77b7

File tree

4 files changed

+37
-46
lines changed

4 files changed

+37
-46
lines changed

src/main/java/org/truffleruby/core/exception/ExceptionNodes.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -239,14 +239,9 @@ private Object readCustomBacktrace(DynamicObject exception) {
239239
}
240240

241241
@NonStandard
242-
@CoreMethod(names = "capture_backtrace!", optional = 1, lowerFixnum = 1)
242+
@CoreMethod(names = "capture_backtrace!", required = 1, lowerFixnum = 1)
243243
public abstract static class CaptureBacktraceNode extends CoreMethodArrayArgumentsNode {
244244

245-
@Specialization
246-
protected Object captureBacktrace(DynamicObject exception, NotProvided offset) {
247-
return captureBacktrace(exception, 1);
248-
}
249-
250245
@Specialization
251246
protected Object captureBacktrace(DynamicObject exception, int offset) {
252247
final Backtrace backtrace = getContext().getCallStack().getBacktrace(this, offset);

src/main/ruby/truffleruby/core/kernel.rb

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -657,8 +657,21 @@ def warn(*messages, uplevel: undefined)
657657
end
658658
module_function :warn
659659

660-
def raise(exc=undefined, msg=undefined, ctx=nil)
661-
Truffle::KernelOperations.internal_raise exc, msg, ctx
660+
def raise(exc = undefined, msg = undefined, ctx = nil)
661+
last = $!
662+
if Primitive.undefined?(exc) and last
663+
exc = last
664+
else
665+
exc = Truffle::KernelOperations.build_exception_for_raise(exc, msg)
666+
667+
exc.set_context ctx if ctx
668+
exc.capture_backtrace!(1) unless exc.backtrace?
669+
Primitive.exception_set_cause exc, last unless Primitive.object_equal(exc, last)
670+
end
671+
672+
Truffle::KernelOperations.show_exception_for_debug(exc, 1) if $DEBUG
673+
674+
Primitive.vm_raise_exception exc
662675
end
663676
module_function :raise
664677

src/main/ruby/truffleruby/core/thread.rb

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -282,30 +282,15 @@ def inspect
282282
end
283283
alias_method :to_s, :inspect
284284

285-
def raise(exc=undefined, msg=undefined, ctx=nil)
285+
def raise(exc = undefined, msg = undefined, ctx = nil)
286286
return nil unless alive?
287287

288-
if Primitive.undefined? exc
289-
exc = RuntimeError.exception ''
290-
elsif exc.respond_to? :exception
291-
if Primitive.undefined? msg
292-
exc = exc.exception
293-
else
294-
exc = exc.exception msg
295-
end
296-
Kernel.raise TypeError, 'exception class/object expected' unless Exception === exc
297-
elsif exc.kind_of? String
298-
exc = RuntimeError.exception exc
299-
else
300-
Kernel.raise TypeError, 'exception class/object expected'
301-
end
288+
exc = Truffle::KernelOperations.build_exception_for_raise(exc, msg)
302289

303290
exc.set_context ctx if ctx
304291
exc.capture_backtrace!(1) unless exc.backtrace?
305292

306-
if $DEBUG
307-
STDERR.puts "Exception: `#{exc.class}' - #{exc.message}"
308-
end
293+
Truffle::KernelOperations.show_exception_for_debug(exc, 1) if $DEBUG
309294

310295
if self == Thread.current
311296
Primitive.vm_raise_exception exc

src/main/ruby/truffleruby/core/truffle/kernel_operations.rb

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -128,41 +128,39 @@ def self.load_error(name)
128128
load_error
129129
end
130130

131-
def self.internal_raise(exc, msg, ctx)
132-
skip = false
131+
def self.build_exception_for_raise(exc, msg)
133132
if Primitive.undefined? exc
134-
exc = $!
135-
if exc
136-
skip = true
137-
else
138-
exc = RuntimeError.new ''
139-
end
133+
::RuntimeError.exception ''
140134
elsif exc.respond_to? :exception
141135
if Primitive.undefined? msg
142136
exc = exc.exception
143137
else
144138
exc = exc.exception msg
145139
end
146-
raise TypeError, 'exception class/object expected' unless exc.kind_of?(Exception)
147-
elsif exc.kind_of? String
148-
exc = RuntimeError.exception exc
140+
141+
exception_class_object_expected! unless Primitive.object_kind_of?(exc, ::Exception)
142+
exc
143+
elsif exc.kind_of? ::String
144+
::RuntimeError.exception exc
149145
else
150-
raise TypeError, 'exception class/object expected'
146+
exception_class_object_expected!
151147
end
148+
end
152149

153-
unless skip
154-
exc.set_context ctx if ctx
155-
exc.capture_backtrace!(2) unless exc.backtrace?
156-
Primitive.exception_set_cause exc, $! unless exc.equal?($!)
157-
end
150+
# Avoid using #raise here to prevent infinite recursion
151+
def self.exception_class_object_expected!
152+
exc = ::TypeError.new('exception class/object expected')
153+
exc.capture_backtrace!(1)
158154

159-
if $DEBUG
160-
STDERR.puts "Exception: `#{exc.class}' #{caller(2, 1)[0]} - #{exc.message}\n"
161-
end
155+
Truffle::KernelOperations.show_exception_for_debug(exc, 2) if $DEBUG
162156

163157
Primitive.vm_raise_exception exc
164158
end
165159

160+
def self.show_exception_for_debug(exc, uplevel)
161+
STDERR.puts "Exception: `#{exc.class}' at #{caller(uplevel + 1, 1)[0]} - #{exc.message}\n"
162+
end
163+
166164
def self.check_last_line(line)
167165
unless Primitive.object_kind_of? line, String
168166
raise TypeError, "$_ value need to be String (#{Truffle::ExceptionOperations.to_class_name(line)} given)"

0 commit comments

Comments
 (0)