Skip to content

Commit

Permalink
[GR-18163] Add rb_syserr_fail_str C function
Browse files Browse the repository at this point in the history
PullRequest: truffleruby/4418
  • Loading branch information
andrykonchin committed Dec 5, 2024
2 parents afce327 + a19a172 commit bcad72e
Show file tree
Hide file tree
Showing 14 changed files with 97 additions and 33 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Compatibility:
* `Thread::SizedQueue#freeze` now raises `TypeError` when called (#3681, @Th3-M4jor).
* Add `Range#reverse_each` (#3681, @andrykonchin).
* Emit a warning when `it` call without arguments is used in a block without parameters (#3681, @andrykonchin).
* Add `rb_syserr_fail_str()` (#3732, @andrykonchin).

Performance:

Expand Down
2 changes: 1 addition & 1 deletion lib/cext/ABI_check.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4
5
2 changes: 1 addition & 1 deletion lib/cext/include/truffleruby/truffleruby-abi-version.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@
// $RUBY_VERSION must be the same as TruffleRuby.LANGUAGE_VERSION.
// $ABI_NUMBER starts at 1 and is incremented for every ABI-incompatible change.

#define TRUFFLERUBY_ABI_VERSION "3.3.5.6"
#define TRUFFLERUBY_ABI_VERSION "3.3.5.7"

#endif
22 changes: 22 additions & 0 deletions spec/ruby/core/exception/system_call_error_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ def initialize
e.should be_an_instance_of(@example_errno_class)
end

it "sets an error message corresponding to an appropriate Errno class" do
e = SystemCallError.new(@example_errno)
e.message.should == 'Invalid argument'
end

it "accepts an optional custom message preceding the errno" do
exc = SystemCallError.new("custom message", @example_errno)
exc.should be_an_instance_of(@example_errno_class)
Expand Down Expand Up @@ -81,6 +86,23 @@ def initialize
SystemCallError.new('foo', 2.9).should == SystemCallError.new('foo', 2)
end

it "treats nil errno as unknown error value" do
SystemCallError.new(nil).should be_an_instance_of(SystemCallError)
end

it "treats nil custom message as if it is not passed at all" do
exc = SystemCallError.new(nil, @example_errno)
exc.message.should == 'Invalid argument'
end

it "sets an 'unknown error' message when an unknown error number" do
SystemCallError.new(-1).message.should =~ /Unknown error(:)? -1/
end

it "adds a custom error message to an 'unknown error' message when an unknown error number and a custom message specified" do
SystemCallError.new("custom message", -1).message.should =~ /Unknown error(:)? -1 - custom message/
end

it "converts to Integer if errno is a Complex convertible to Integer" do
SystemCallError.new('foo', Complex(2.9, 0)).should == SystemCallError.new('foo', 2)
end
Expand Down
8 changes: 8 additions & 0 deletions spec/ruby/optional/capi/ext/kernel_spec.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,13 @@ VALUE kernel_spec_rb_syserr_fail(VALUE self, VALUE err, VALUE msg) {
return Qnil;
}

VALUE kernel_spec_rb_syserr_fail_str(VALUE self, VALUE err, VALUE msg) {
if (self != Qundef) {
rb_syserr_fail_str(NUM2INT(err), msg);
}
return Qnil;
}

VALUE kernel_spec_rb_warn(VALUE self, VALUE msg) {
rb_warn("%s", StringValuePtr(msg));
return Qnil;
Expand Down Expand Up @@ -415,6 +422,7 @@ void Init_kernel_spec(void) {
rb_define_method(cls, "rb_catch_obj", kernel_spec_rb_catch_obj, 2);
rb_define_method(cls, "rb_sys_fail", kernel_spec_rb_sys_fail, 1);
rb_define_method(cls, "rb_syserr_fail", kernel_spec_rb_syserr_fail, 2);
rb_define_method(cls, "rb_syserr_fail_str", kernel_spec_rb_syserr_fail_str, 2);
rb_define_method(cls, "rb_warn", kernel_spec_rb_warn, 1);
rb_define_method(cls, "rb_yield", kernel_spec_rb_yield, 1);
rb_define_method(cls, "rb_yield_indirected", kernel_spec_rb_yield_indirected, 1);
Expand Down
30 changes: 28 additions & 2 deletions spec/ruby/optional/capi/kernel_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,39 @@ class CApiKernelSpecs::Exc < StandardError
it "raises an exception from the given error" do
-> do
@s.rb_syserr_fail(Errno::EINVAL::Errno, "additional info")
end.should raise_error(Errno::EINVAL, /additional info/)
end.should raise_error(Errno::EINVAL, "Invalid argument - additional info")
end

it "can take a NULL message" do
-> do
@s.rb_syserr_fail(Errno::EINVAL::Errno, nil)
end.should raise_error(Errno::EINVAL)
end.should raise_error(Errno::EINVAL, "Invalid argument")
end

it "uses an 'unknown error' message when errno is unknown" do
-> do
@s.rb_syserr_fail(-10, nil)
end.should raise_error(SystemCallError, /Unknown error(:)? -1/) # a new class Errno::E-01 is generated on the fly
end
end

describe "rb_syserr_fail_str" do
it "raises an exception from the given error" do
-> do
@s.rb_syserr_fail_str(Errno::EINVAL::Errno, "additional info")
end.should raise_error(Errno::EINVAL, "Invalid argument - additional info")
end

it "can take nil as a message" do
-> do
@s.rb_syserr_fail_str(Errno::EINVAL::Errno, nil)
end.should raise_error(Errno::EINVAL, "Invalid argument")
end

it "uses an 'unknown error' message when errno is unknown" do
-> do
@s.rb_syserr_fail_str(-1, nil)
end.should raise_error(SystemCallError, /Unknown error(:)? -1/) # a new class Errno::E-01 is generated on the fly
end
end

Expand Down
8 changes: 7 additions & 1 deletion src/main/c/cext/exception.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,13 @@ VALUE rb_errinfo(void) {
}

void rb_syserr_fail(int eno, const char *message) {
polyglot_invoke(RUBY_CEXT, "rb_syserr_fail", eno, rb_tr_unwrap(rb_str_new_cstr(message == NULL ? "" : message)));
VALUE messageValue = (message == NULL) ? Qnil : rb_str_new_cstr(message);
polyglot_invoke(RUBY_CEXT, "rb_syserr_fail", eno, rb_tr_unwrap(messageValue));
UNREACHABLE;
}

void rb_syserr_fail_str(int eno, VALUE message) {
polyglot_invoke(RUBY_CEXT, "rb_syserr_fail", eno, rb_tr_unwrap(message));
UNREACHABLE;
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/truffleruby/cext/CExtNodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -1313,7 +1313,7 @@ public abstract static class RbSysErrFail extends CoreMethodArrayArgumentsNode {
Object rbSysErrFail(int errno, Object string,
@Cached ErrnoErrorNode errnoErrorNode) {
final Backtrace backtrace = getContext().getCallStack().getBacktrace(this);
throw new RaiseException(getContext(), errnoErrorNode.execute(null, errno, string, backtrace));
throw new RaiseException(getContext(), errnoErrorNode.execute(null, errno, string, null, backtrace));
}

}
Expand Down
22 changes: 14 additions & 8 deletions src/main/java/org/truffleruby/core/exception/ErrnoErrorNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.truffleruby.core.klass.RubyClass;
import org.truffleruby.core.string.RubyString;
import org.truffleruby.core.string.ImmutableRubyString;
import org.truffleruby.language.Nil;
import org.truffleruby.language.RubyBaseNode;
import org.truffleruby.language.backtrace.Backtrace;
import org.truffleruby.language.dispatch.DispatchNode;
Expand All @@ -33,10 +34,11 @@ public static ErrnoErrorNode create() {
@Child private DispatchNode formatMessageNode;

public abstract RubySystemCallError execute(RubyClass rubyClass, int errno, Object extraMessage,
Backtrace backtrace);
Object location, Backtrace backtrace);

@Specialization
RubySystemCallError errnoError(RubyClass rubyClass, int errno, Object extraMessage, Backtrace backtrace,
RubySystemCallError errnoError(
RubyClass rubyClass, int errno, Object extraMessage, Object location, Backtrace backtrace,
@Cached TruffleString.FromJavaStringNode fromJavaStringNode) {
final String errnoName = getContext().getCoreLibrary().getErrnoName(errno);

Expand All @@ -57,26 +59,30 @@ RubySystemCallError errnoError(RubyClass rubyClass, int errno, Object extraMessa
Encodings.UTF_8);
}

final RubyString errorMessage = formatMessage(errnoDescription, errno, extraMessage);
final RubyString errorMessage = formatMessage(errnoDescription, errno, extraMessage, location);

return ExceptionOperations
.createSystemCallError(getContext(), errnoClass, errorMessage, errno, backtrace);
}

private RubyString formatMessage(Object errnoDescription, int errno, Object extraMessage) {
assert extraMessage instanceof RubyString || extraMessage instanceof ImmutableRubyString;
private RubyString formatMessage(Object errnoDescription, int errno, Object extraMessage, Object location) {
assert extraMessage instanceof RubyString || extraMessage instanceof ImmutableRubyString ||
extraMessage instanceof Nil;

if (formatMessageNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
formatMessageNode = insert(DispatchNode.create());
}

if (location == null) {
location = nil;
}

var arguments = new Object[]{ errnoDescription, errno, extraMessage, location };
return (RubyString) formatMessageNode.call(
getContext().getCoreLibrary().truffleExceptionOperationsModule,
"format_errno_error_message",
errnoDescription,
errno,
extraMessage);
arguments);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,8 @@ public abstract static class ExceptionErrnoErrorPrimitiveNode extends PrimitiveA
@Child ErrnoErrorNode errnoErrorNode = ErrnoErrorNode.create();

@Specialization
RubySystemCallError exceptionErrnoError(RubyClass errorClass, Object message, int errno) {
return errnoErrorNode.execute(errorClass, errno, message, null);
RubySystemCallError exceptionErrnoError(RubyClass errorClass, Object extraMessage, int errno, Object location) {
return errnoErrorNode.execute(errorClass, errno, extraMessage, location, null);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -546,11 +546,11 @@ public static TStringBuilder formatToTStringBuilder(Token[] compiledPattern, Zon
} catch (IndexOutOfBoundsException ioobe) {
final Backtrace backtrace = context.getCallStack().getBacktrace(currentNode);
final RubyString message = StringOperations.createUTF8String(context, language, "strftime");
final int errno = context.getCoreLibrary().getErrnoValue("ERANGE");

throw new RaiseException(
context,
errnoErrorNode.execute(null, context.getCoreLibrary().getErrnoValue("ERANGE"), message,
backtrace));
errnoErrorNode.execute(null, errno, message, null, backtrace));
}

// reset formatter
Expand Down
13 changes: 4 additions & 9 deletions src/main/ruby/truffleruby/core/exception.rb
Original file line number Diff line number Diff line change
Expand Up @@ -274,12 +274,6 @@ def success?

class SystemCallError < StandardError

def self.errno_error(klass, message, errno, location)
message = message ? " - #{message}" : +''
message = " @ #{location}#{message}" if location
Primitive.exception_errno_error klass, message, errno
end

# We use .new here because when errno is set, we attempt to
# lookup and return a subclass of SystemCallError, specifically,
# one of the Errno subclasses.
Expand All @@ -298,7 +292,8 @@ def self.new(*args)
message = nil
else
errno = nil
message = StringValue(args.first)
message = args.first
message = StringValue(message) unless Primitive.nil?(message)
end
location = nil
when 2
Expand All @@ -314,7 +309,7 @@ def self.new(*args)
# If it corresponds to a known Errno class, create and return it now
if errno
errno = Primitive.rb_num2long(errno)
error = SystemCallError.errno_error(self, message, errno, location)
error = Primitive.exception_errno_error(self, message, errno, location)
return error unless Primitive.nil? error
end
super(message, errno, location)
Expand All @@ -333,7 +328,7 @@ def self.new(*args)
end

if defined?(self::Errno) && Primitive.is_a?(self::Errno, Integer)
error = SystemCallError.errno_error(self, message, self::Errno, location)
error = Primitive.exception_errno_error(self, message, self::Errno, location)
if error && Primitive.equal?(Primitive.class(error), self)
return error
end
Expand Down
11 changes: 5 additions & 6 deletions src/main/ruby/truffleruby/core/truffle/exception_operations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -352,12 +352,11 @@ def self.comparison_error_message(x, y)
format("super: no superclass method `%s'", exception.name)
end

def self.format_errno_error_message(errno_description, errno, extra_message)
if Primitive.nil? errno_description
"unknown error: #{errno} - #{extra_message}"
else
"#{errno_description}#{extra_message}"
end
def self.format_errno_error_message(errno_description, errno, extra_message, location)
message = errno_description || "Unknown error: #{errno}"
message += " @ #{location}" if location
message += " - #{extra_message}" if extra_message
+message
end
end
end
1 change: 1 addition & 0 deletions tool/generate-cext-trampoline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
rb_exc_raise
rb_jump_tag
rb_syserr_fail
rb_syserr_fail_str
rb_sys_fail
rb_sys_fail_str
rb_throw
Expand Down

0 comments on commit bcad72e

Please sign in to comment.