truffleruby icon indicating copy to clipboard operation
truffleruby copied to clipboard

Unexpected return (LocalJumpError)

Open gogainda opened this issue 4 years ago • 6 comments

throws an exception - unexpected return (LocalJumpError)

text = "not_ruby"
def parseable? text
  verbose, $VERBOSE = $VERBOSE, nil
  eval("BEGIN {return true}\n#{text}")
rescue SyntaxError
  false
ensure
  $VERBOSE = verbose
end

content = if parseable? text then
              "parseable"
          end
puts content

suprisingly MRI Ruby returns parseable which is obviously not

The code about is simplified version of this https://github.com/ruby/rdoc/blob/master/lib/rdoc/markup/to_html.rb#L214

gogainda avatar Sep 03 '21 13:09 gogainda

Interesting, RDoc uses yet another variant to check if it's valid Ruby syntax: https://github.com/ruby/rdoc/blob/2f7dfecdc3c90477e2f77e8cbadfba9cbe444814/lib/rdoc/markup/to_html.rb#L427-L434 This is what IRB uses now: https://github.com/ruby/irb/pull/134/files/5eb3ef3293a026e3f7de1711b8a399fe736bdb13#r702901657

So this issue is about supporting return in BEGIN {} in eval, or maybe simply return in eval, which I think returns from the parseable? method, not just from the eval.

eregon avatar Sep 06 '21 13:09 eregon

This also happens in bundle exec rake --trace doc of https://github.com/msgpack/msgpack-ruby/runs/6416790321?check_suite_focus=true:

$ bundle exec rake --trace doc
** Invoke doc (first_time)
** Execute doc
[warn]: @param tag has unknown parameter name: obj 
    in file `doclib/msgpack/factory.rb' near line 141
rake aborted!
LocalJumpError: unexpected return
(eval):1:in `parseable?'
/home/eregon/.rubies/truffleruby-dev/lib/mri/rdoc/markup/to_html.rb:429:in `parseable?'
/home/eregon/.rubies/truffleruby-dev/lib/mri/rdoc/markup/to_html.rb:214:in `accept_verbatim'
/home/eregon/.rubies/truffleruby-dev/lib/mri/rdoc/markup/verbatim.rb:26:in `accept'
/home/eregon/.rubies/truffleruby-dev/lib/mri/rdoc/markup/formatter.rb:75:in `block in accept_document'
/home/eregon/.rubies/truffleruby-dev/lib/mri/rdoc/markup/formatter.rb:70:in `each'
/home/eregon/.rubies/truffleruby-dev/lib/mri/rdoc/markup/formatter.rb:70:in `accept_document'
/home/eregon/.rubies/truffleruby-dev/lib/mri/rdoc/markup/document.rb:68:in `accept'
/home/eregon/.rubies/truffleruby-dev/lib/mri/rdoc/markup.rb:822:in `convert'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/helpers/markup/rdoc_markup.rb:54:in `block in to_html'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/helpers/markup/rdoc_markup.rb:52:in `synchronize'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/helpers/markup/rdoc_markup.rb:52:in `to_html'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/helpers/html_helper.rb:95:in `html_markup_markdown'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/helpers/html_helper.rb:62:in `htmlify'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/templates/default/layout/html/setup.rb:67:in `diskfile'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:367:in `render_section'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:259:in `block (2 levels) in run'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:256:in `each'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:256:in `block in run'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:398:in `add_options'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:255:in `run'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:277:in `block in yieldall'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:412:in `with_section'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:277:in `yieldall'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/templates/default/layout/html/layout.erb:21:in `_erb_cache_5'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:287:in `erb'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/templates/default/layout/html/setup.rb:62:in `layout'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:367:in `render_section'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:259:in `block (2 levels) in run'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:256:in `each'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:256:in `block in run'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:398:in `add_options'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:255:in `run'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:136:in `run'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/templates/default/fulldoc/html/setup.rb:52:in `block in serialize_index'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/engine.rb:123:in `block in with_serializer'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/logging.rb:82:in `capture'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/engine.rb:121:in `with_serializer'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/templates/default/fulldoc/html/setup.rb:51:in `serialize_index'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/templates/default/fulldoc/html/setup.rb:68:in `serialize_file'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/templates/default/fulldoc/html/setup.rb:11:in `block in init'
<internal:core> core/enumerable.rb:355:in `each_with_index'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/templates/default/fulldoc/html/setup.rb:10:in `init'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:193:in `initialize'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:131:in `new'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/template.rb:136:in `run'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/templates/engine.rb:105:in `generate'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/cli/yardoc.rb:357:in `run_generate'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/cli/yardoc.rb:267:in `run'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/yard-0.9.27/lib/yard/rake/yardoc_task.rb:74:in `block in define'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/rake-13.0.6/lib/rake/task.rb:281:in `block in execute'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/rake-13.0.6/lib/rake/task.rb:281:in `each'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/rake-13.0.6/lib/rake/task.rb:281:in `execute'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/rake-13.0.6/lib/rake/task.rb:219:in `block in invoke_with_call_chain'
/home/eregon/.rubies/truffleruby-dev/lib/mri/monitor.rb:218:in `mon_synchronize'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/rake-13.0.6/lib/rake/task.rb:199:in `invoke_with_call_chain'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/rake-13.0.6/lib/rake/task.rb:188:in `invoke'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/rake-13.0.6/lib/rake/application.rb:160:in `invoke_task'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/rake-13.0.6/lib/rake/application.rb:116:in `block (2 levels) in top_level'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/rake-13.0.6/lib/rake/application.rb:116:in `each'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/rake-13.0.6/lib/rake/application.rb:116:in `block in top_level'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/rake-13.0.6/lib/rake/application.rb:125:in `run_with_threads'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/rake-13.0.6/lib/rake/application.rb:110:in `top_level'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/rake-13.0.6/lib/rake/application.rb:83:in `block in run'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/rake-13.0.6/lib/rake/application.rb:186:in `standard_exception_handling'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/rake-13.0.6/lib/rake/application.rb:80:in `run'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/rake-13.0.6/exe/rake:27:in `<top (required)>'
<internal:core> core/kernel.rb:376:in `load'
<internal:core> core/kernel.rb:376:in `load'
/home/eregon/.rubies/truffleruby-dev/bin/rake:25:in `<top (required)>'
<internal:core> core/kernel.rb:376:in `load'
<internal:core> core/kernel.rb:376:in `load'
/home/eregon/.rubies/truffleruby-dev/lib/mri/bundler/cli/exec.rb:58:in `kernel_load'
/home/eregon/.rubies/truffleruby-dev/lib/mri/bundler/cli/exec.rb:23:in `run'
/home/eregon/.rubies/truffleruby-dev/lib/mri/bundler/cli.rb:478:in `exec'
/home/eregon/.rubies/truffleruby-dev/lib/mri/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
/home/eregon/.rubies/truffleruby-dev/lib/mri/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'
/home/eregon/.rubies/truffleruby-dev/lib/mri/bundler/vendor/thor/lib/thor.rb:392:in `dispatch'
/home/eregon/.rubies/truffleruby-dev/lib/mri/bundler/cli.rb:31:in `dispatch'
/home/eregon/.rubies/truffleruby-dev/lib/mri/bundler/vendor/thor/lib/thor/base.rb:485:in `start'
/home/eregon/.rubies/truffleruby-dev/lib/mri/bundler/cli.rb:25:in `start'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/bundler-2.2.32/libexec/bundle:49:in `block in <top (required)>'
/home/eregon/.rubies/truffleruby-dev/lib/mri/bundler/friendly_errors.rb:103:in `with_friendly_errors'
/home/eregon/.rubies/truffleruby-dev/lib/gems/gems/bundler-2.2.32/libexec/bundle:37:in `<top (required)>'
<internal:core> core/kernel.rb:376:in `load'
<internal:core> core/kernel.rb:376:in `load'
/home/eregon/.rubies/truffleruby-dev/bin/bundle:42:in `<main>'
Tasks: TOP => doc

Seems the exact same issue.

eregon avatar May 13 '22 09:05 eregon

Some idea to fix this, we'd store the ReturnID in FrameDescriptor#getInfo(), and so when parsing the eval we could find the proper ReturnID to jump to.

Semantics are like (i.e., the return inside the eval returns to the surrounding method, and not allowed top-level):

$ ruby -e 'def m; eval "return 4*3"; p :after; end; p m'            
12
$ ruby -e 'def m; proc { eval "return 4*3"; p :proc; }.call; p :after; end; p m'
12
$ ruby -e 'p eval "return 4*3"'                                         
(eval):1:in `<main>': unexpected return (LocalJumpError)

eregon avatar May 13 '22 10:05 eregon

When we implement this we should reenable rdoc CI: https://github.com/ruby/rdoc/pull/1029

eregon avatar Jul 24 '23 15:07 eregon

Another idea, because it's quite complicated to store ReturnID for methods & lambda in FrameDescriptor without a lot of changes or performance impact:

  • We could handle return in BEGIN {} in eval specially, and always use a LocalReturnException.

eregon avatar Jul 26 '23 11:07 eregon

https://github.com/ruby/rdoc/pull/1032 so rdoc uses the more portable pattern eval("BEGIN { throw :valid, true }\n#{code}")

eregon avatar Jul 26 '23 12:07 eregon