Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Kernel#exit #1972

Open
somedevfox opened this issue Jul 24, 2022 · 8 comments
Open

Implement Kernel#exit #1972

somedevfox opened this issue Jul 24, 2022 · 8 comments
Labels
A-crate-features Area: Compile-time features or attributes. A-ruby-core Area: Ruby Core types. C-enhancement Category: New feature or request.

Comments

@somedevfox
Copy link

Kernel#exit initiates a termination of application with a custom exit status (if set, status argument is optional and it's default value is true) by raising SystemExit exception.

Motivation behind method

ArtichokeRuby supports both native and wasm targets and for native, it's sometimes useful to have an ability to exit application in case of:

  • Exit prompt for UI Applications
  • Errors like missing argument in CLI/UI Applications
  • Termination of program to aid plot of certain games which use Ruby as scripting back-end (such as OneShot and one of it's mods, OneShot: Fading Memory, powered by MKXP Engine)

Implementation

After implementing a method, it can be wired up to Artichoke like this:

  • unsafe extern "C" fn kernel_print(mrb: *mut sys::mrb_state, _slf: sys::mrb_value) -> sys::mrb_value {
    let args = mrb_get_args!(mrb, *args);
    unwrap_interpreter!(mrb, to => guard);
    let args = args.iter().copied().map(Value::from);
    let result = trampoline::print(&mut guard, args);
    match result {
    Ok(value) => value.inner(),
    Err(exception) => error::raise(guard, exception),
    }
    }
  • pub fn print<T>(interp: &mut Artichoke, args: T) -> Result<Value, Error>
    where
    T: IntoIterator<Item = Value>,
    {
    for value in args {
    let display = value.to_s(interp);
    interp.print(&display)?;
    }
    Ok(Value::nil())
    }

    One should take args Value and convert it to i32 using artichoke_backend::convert::implicitly_convert_to_int(interp, value)? as i32; method.

Implementation Notes

Method should be locked under a feature.

@lopopolo
Copy link
Member

sounds great @somedevfox.

One other bit of implementation to help you along, since the argument to Kernel#exit is optional, you'll want to extract it in the C entrypoint like this:

let num = mrb_get_args!(mrb, optional = 1);
unwrap_interpreter!(mrb, to => guard);
let array = Value::from(ary);
let num = num.map(Value::from);

@lopopolo
Copy link
Member

This looks great @somedevfox. Feel free to get started working on this. If you have any questions, please reach out either in this ticket or on Discord.

@lopopolo lopopolo added A-ruby-core Area: Ruby Core types. A-crate-features Area: Compile-time features or attributes. labels Jul 24, 2022
@lopopolo lopopolo added the C-enhancement Category: New feature or request. label Jul 24, 2022
@lopopolo
Copy link
Member

lopopolo commented Jul 24, 2022

  • One should take args Value and convert it to i32 using artichoke_backend::convert::implicitly_convert_to_int(interp, value)? as i32; method.

It looks like we'll need to do a fallible conversion to i32 with i32::try_from since MRI blows up if the arg is too big:

$ ruby -e 'exit(2**64 + 1)'
-e:1:in `exit': bignum too big to convert into `long' (RangeError)
        from -e:1:in `<main>'
$ ruby -e 'exit(2**64)'
-e:1:in `exit': bignum too big to convert into `long' (RangeError)
        from -e:1:in `<main>'
$ ruby -e 'exit(2**64-1)'
-e:1:in `exit': bignum too big to convert into `long' (RangeError)
        from -e:1:in `<main>'
$ ruby -e 'exit(2**61)'
-e:1:in `exit': integer 2305843009213693952 too big to convert to `int' (RangeError)
        from -e:1:in `<main>'
$ ruby -e 'exit(2**32)'
-e:1:in `exit': integer 4294967296 too big to convert to `int' (RangeError)
        from -e:1:in `<main>'
$ ruby -e 'exit(2**31)'
-e:1:in `exit': integer 2147483648 too big to convert to `int' (RangeError)
        from -e:1:in `<main>'
$ ruby -e 'exit(2**31-1)'
$ echo $?
255

@lopopolo
Copy link
Member

hi @somedevfox, checking in here. Are you still working on this?

@somedevfox
Copy link
Author

Ah, yes, I am-
Sorry for.. long wait.
I've postponed this issue until I'm done with other things and such, so...

One issue I ran into when I tried to implement #exit is that raising an exception would result in code after the raise being completely ignored.
I would like to stop MRuby from running, but this issue doesn't let me, is there a way to perhaps bypass this?

@lopopolo
Copy link
Member

thanks for checking in @somedevfox no worries on the wait 😄

You'll want to check these places in the Ruby CLI entrypoint:

artichoke/src/ruby.rs

Lines 133 to 147 in 4f03839

if !args.commands.is_empty() {
execute_inline_eval(interp, error, args.commands, args.fixture.as_deref())
} else if let Some(programfile) = args.programfile.filter(|file| file != Path::new("-")) {
execute_program_file(interp, error, programfile.as_path(), args.fixture.as_deref())
} else {
let mut program = vec![];
input
.read_to_end(&mut program)
.map_err(|_| IOError::from("Could not read program from STDIN"))?;
if let Err(ref exc) = interp.eval(program.as_slice()) {
backtrace::format_cli_trace_into(error, interp, exc)?;
return Ok(Err(()));
}
Ok(Ok(()))
}

We'll probably have to check for SystemExit here and get the code out of it, maybe with a call to exc.funcall(interp, "code", &[], None)?

The return value for the entrypoint will need to be tweaked to somehow return the exit code.

@lopopolo
Copy link
Member

if you're interested in adding a test for this behavior, you can check in the ui-tests suite like this one which tests ARGV parsing:

#[test]
fn e_flag_argv() {
insta::assert_toml_snapshot!(run(BINARY, &["-e", "puts ARGV.inspect", "a", "b", "c"]).unwrap());
}

@lopopolo
Copy link
Member

Hi @somedevfox, checking in here. Since its been a while I'm going to unassign this ticket from you. You're still free to work on it :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-crate-features Area: Compile-time features or attributes. A-ruby-core Area: Ruby Core types. C-enhancement Category: New feature or request.
Development

No branches or pull requests

2 participants